jfowkes
jfowkes

Reputation: 1565

How can I mock a class and provide a unique object for each set of constructor parameters?

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

Answers (1)

Uriya Harpeness
Uriya Harpeness

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

Related Questions