There is no need to provide a separate constructor. Mocking patches your code to replace objects with mocks. Just use the mock.patch() decorator on your test methods; it'll pass in references to the generated mock objects.
Both producer.Producer() and consumer.Consumer() are then mocked out before you create the instance:
from unittest import mock
class MyTest(unittest.TestCase):
@mock.patch('producer.Producer', autospec=True)
@mock.patch('consumer.Consumer', autospec=True)
def test_send_data(self, mock_consumer, mock_producer):
# configure the consumer instance run method
consumer_instance = mock_consumer.return_value
consumer_instance.run.return_value = [
{u'dataObjectID': u'test1'},
{u'dataObjectID': u'test2'},
{u'dataObjectID': u'test3'}]
coor = MyCoordinator()
# Create some data and send it to the producer
for count in range(3):
coor.sendData('test{}'.format(count) , None)
# Now verify that the mocks have been called correctly
mock_producer.assert_has_calls([
mock.Call('test1', None),
mock.Call('test2', None),
mock.Call('test3', None)])
So the moment test_send_data is called, the mock.patch() code replaces the producer.Producer reference with a mock object. Your MyCoordinator class then uses those mock objects rather than the real code. calling producer.Producer() returns a new mock object (the same object that mock_producer.return_value references), etc.
I've made the assumption that producer and consumer are top-level module names. If they are not, provide the full import path. From the mock.patch() documentation:
Answer from Martijn Pieters on Stack Overflowtarget should be a string in the form
'package.module.ClassName'. The target is imported and the specified object replaced with the new object, so the target must be importable from the environment you are callingpatch()from. The target is imported when the decorated function is executed, not at decoration time.
Since you're only interested in testing one method, why don't you just mock the entire HoldsData class and pin on it the x_coords method?
>>> mock = MagicMock(data={'points': [(0,1), (2,3), (4,5)]})
>>> mock.x_coords = HoldsData.__dict__['x_coords']
>>> mock.x_coords(mock)
[0, 2, 4]
This way you'll have full control over the x_coords input and output (either by side effect or return value).
Note: In py3k you could just do mock.x_coords = HoldsData.x_coords since there are no more unbound methods.
That could also be done in the constructor of the mock object:
MagicMock(data={'points': [(0,1), (2,3), (4,5)]}, x_coords=HoldsData.__dict__['x_coords'])
You're basically running into this issue due to this:
A test is not a unit test if:
- It touches the filesystem
If you wish to follow this rule, you should modify the _parse method. In particular, it should not take a file as input. The task of _parse is to parse the data but where the data comes from is not the concern of that method.
You could have a string that contains the same data which is then passed to _parse. Similarly, the data could come from a database or somewhere else entirely. When _parse only takes the data as input, you can unit test that method in a much easier way.
It would look something like this:
class HoldsData(object):
def __init__(self, path):
self.data = {}
file_data = self._read_data_from_file(path)
self.data.update(self._parse(file_data))
def _read_data_from_file(self, path):
# read data from file
return data
def _parse(self, data):
# do parsing
Clean code leads to clean tests of course. The best case would be to mock the data and to provide _parse with input and then test x_coords later. If that's not possible, I'd leave it as it is. If your six lines of mock code are the only part of the test cases that you're worried about, then you're fine.
You have to patch the name, not the instance.
From the official Python documentation: Where to patch
patch() works by (temporarily) changing the object that a name points to with another one. There can be many names pointing to any individual object, so for patching to work you must ensure that you patch the name used by the system under test.
In your example, your class Foo is defined in a module foomod.py, so you have to patch foomod.Bar instead of mod.Bar.
You can put this into a fixture by using the mocker fixture from pytest-mock or with unittest.mock.patch.
@pytest.fixture # With pytest-mock
def mock_bar(mocker):
return mocker.patch('foomod.Bar')
@pytest.fixture # With stdlib
def mock_bar():
with patch('foomod.Bar') as mock:
yield mock
# Usage
def test_foo(mock_bar):
pass
To my knowledge, there is no significant difference between the two approaches. Both are cleaned up when the fixture goes out of scope.
I use the following to mock objects with pytest without the fixture decorator
# path.to.MyClass
class MyClass():
def __init__(self, some_parameter: SomeObject) -> None:
self.some_value = some_parameter
def get_something(self) -> str:
return 'Hello'
# tests.py
from pytest_mock.plugin import MockerFixture
from unittest.mock import MagicMock
def test_with_mock(mocker: MockerFixture) -> None:
mock_myclass: MagicMock = mocker.patch('path.to.MyClass')
mock_myclass_get_something: MagicMock = mocker.patch('path.to.MyClass.get_something')
mock_myclass_get_something.return_value = 'World!'
assert mock_myclass.get_something() == 'World!'