Reputation: 1565
I am adding unit tests to a legacy system.
There is a Manager
class that creates Worker
objects and performs various operations on them.
I want to mock these workers and verify that the manager is creating and using them correctly.
To do this I need to be able to create unique mock objects based on the inputs to the constructor.
I have not been able to find the right combination of MagicMock
, patch
etc. to make this happen.
So far I have the following which allows me to return a fixed list of custom mock objects for a test:
def get_worker_mock(name, param1):
worker = MagicMock(spec=Worker)
worker.name = name
worker.param1 = param1
worker.method1.return_value = "mock_method1_val"
worker.method2.return_value = "mock_method2_val"
return worker
@pytest.fixture(autouse=True)
def worker_fixture():
with patch("mypackage.manager.Worker", spec=Worker) as fixture:
worker1 = get_worker_mock("Worker1", 1)
worker2 = get_worker_mock("Worker2", 2)
fixture.side_effect = [worker1, worker2]
yield fixture
This allows me to run tests assuming that the manager intends to create worker1
followed by worker2
. This is fine, but inflexible.
What I would like is for the worker_fixture
to be able to construct a unique MagicMock object on the fly based on a call to the Worker
constructor, which may occur several times in a test.
The manager class and its tests rely on certain specific properties and methods of the worker, hence I don't believe I can just patch the class like so:
@patch("mypackage.Worker", autospec=True)
def test_some_manager_test(self, worker_patch):
# Test using the mock...
How can I flexibly create mock objects like this?
Upvotes: 0
Views: 291
Reputation: 628
I would patch the class' contructor, to call the get_worker_mock
, keep the control of the object instances and patches in your side, I dont think @patch
can give you all the behavior you desire.
Create a fixture to do so.
@pytest.fixture
def patch_worker(monkeypatch):
monkeypatch.setattr(mypackage.manager, 'Worker', get_worker_mock)
Upvotes: 1