Мой пример использования таков: я хочу, чтобы прибор Pytest возвращал класс, зависимость которого была исключена, чтобы вернуть другое настраиваемое значение.
Объектом тестирования является класс Subject
.Это зависит от модуля boto3
.Я хочу сделать это так, чтобы при вызове boto3.client('codepipeline')
мне возвращался фиктивный объект, настроенный на определенное поведение.
В целях тестирования я хочу вызвать для него функцию 'foo ()'и вернуть настраиваемый ответ.
В этом случае я настраиваю его для возврата строки 'bar'.
Я пытаюсь использовать unittest.mock.patch в качестве диспетчера контекста внутриприбор pytest и он не работает.
Демонстрационный код:
#subject.py
import boto3
class Subject:
def __init__(self):
self.codepipeline_client = boto3.client('codepipeline')
def foo(self):
return self.codepipeline_client.foo()
#test_subject.py
from unittest.mock import patch, NonCallableMagicMock
import pytest
from expects import expect, equal
import subject
@pytest.fixture
def mocked_subject_factory():
def _boto3_client_mock(aws_service, foo):
client = None
if aws_service == 'codepipeline':
client = NonCallableMagicMock(name='codepipeline_client')
client.foo.return_value = foo
return client
def _factory(foo):
with patch('subject.boto3.client') as mock_client:
mock_client.side_effect = _boto3_client_mock(foo=foo)
yield subject.Subject()
return _factory
class TestSubject:
def test_passing_foo(self, mocked_subject_factory):
s = mocked_subject_factory(foo='mocked foo value')
print(s)
expect(s.foo()).to(equal('mocked foo value'))
Если я запускаю pytest -s test_subject.py
, я получаю:
================================================= test session starts ==================================================
platform linux -- Python 3.7.0, pytest-3.8.2, py-1.6.0, pluggy-0.7.1
rootdir: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX, inifile:
collected 1 item
test_subject.py <generator object mocked_subject_factory.<locals>._factory at 0x7f246255d840>
F
======================================================= FAILURES =======================================================
_____________________________________________ TestSubject.test_passing_foo _____________________________________________
self = <test_subject.TestSubject object at 0x7f24624e0940>
mocked_subject_factory = <function mocked_subject_factory.<locals>._factory at 0x7f24624e36a8>
def test_passing_foo(self, mocked_subject_factory):
s = mocked_subject_factory(foo='mocked foo value')
print(s)
> expect(s.foo()).to(equal('mocked foo value'))
E AttributeError: 'generator' object has no attribute 'foo'
test_subject.py:31: AttributeError
РЕДАКТИРОВАТЬ 1: Я следовал @инструкции hoefling из комментариев, приведшие к следующему тесту, но получили тот же результат:
from unittest.mock import patch, NonCallableMagicMock
import pytest
from expects import expect, equal
import subject
@pytest.fixture
def mocked_subject_factory():
def _boto3_client_mock(aws_service, foo):
client = None
if aws_service == 'codepipeline':
client = NonCallableMagicMock(name='codepipeline_client')
client.foo.return_value = foo
return client
with patch('subject.boto3.client') as mock_client:
def _factory(foo):
mock_client.side_effect = _boto3_client_mock(foo=foo)
yield subject.Subject()
yield _factory
class TestSubject:
def test_passing_foo(self, mocked_subject_factory):
print(mocked_subject_factory)
s = mocked_subject_factory(foo='mocked foo value')
print(s)
expect(s.foo()).to(equal('mocked foo value'))