简体   繁体   中英

How to monkeypatch locally imported module for Python testing with pytest?

I am working with a very large project and there are already a lot of test that use pytest 's monkeypatch fixture. I wish to patch a specific method from a class which belongs to an imported module for example:

from project.common import services

in the services package there is a class with a method I wish to patch for example:

services.utils.Calculations.get_area()

I try to mock and monkeypatch it:

mocked_get_area_method= Mock(return_value=500)
monkeypatch.setattr(
   'services.utils.Calculations.get_area',
    mocked_get_user_ip_method,
)

then I create an object in my test:

class TestCommon:
    def test_calculations(self,monkeypatch):
        mocked_get_area_method= Mock(return_value=500)
        monkeypatch.setattr(
           'services.utils.Calculations.get_area',
            mocked_get_user_ip_method,
        )
        calculations = services.utils.Calculations()
        calculations.get_area()
        mocked_get_user_ip_method.assert_called_once()

but I get an error saying: ModuleNotFoundError: No module named 'services'.

I believe the error comes form the fact that maybe monkeypatch looks for objects starting from the high level main project folder. If i try to monkeypath with this path:

        monkeypatch.setattr(
           'project.common.services.utils.Calculations.get_area',
            mocked_get_user_ip_method,
        )

the monkeypatching works BUT then I dont get a True in my assert because I believe the monkeypatching has changed the object in the main projects but since I have already imported it and instantiate the services.utils.Calculations() from the locally imported module the patching does not work.

How can I make this work?

Note: The pytest is run from the main project directory.

You need to apply the patch to the module that has the from project.common import services statement. So for example, assume I have a file project/frontend.py that looks like this:

from project.common import services


def a_method():
    calc = services.utils.Calculations()
    return calc.get_area()

I'd like to test that a_method correctly returns whatever value it receives from the get_area() call, so in tests/test_frontend.py I might write:

import project.frontend

from unittest import mock


def test_a_method(monkeypatch):
    fake_get_area = mock.Mock(return_value=2.0)

    monkeypatch.setattr(
        project.frontend.services.utils.Calculations, "get_area", fake_get_area
    )

    res = project.frontend.a_method()

    assert res == 2.0

Here, we're patching project.frontend.services.utils.Calculations because we've imported project.frontend and frontend.py imports the project.common.services as the name services .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM