Home > Software design >  PyTest: Patch a method where the module was imported using 'importlib.import_module()'
PyTest: Patch a method where the module was imported using 'importlib.import_module()'

Time:06-08

How can I patch a method in a class, where the module containing the class is imported using importlib.import_module()?

The reason for using importlib.import_module() is because the package to which the module belongs contains a -, and as I don't control the package I am unable to change that.

I have attempted to patch the method by targeting it directly, and by targeting it though the name of the imported module, but neither works and the assertion in test_someclass.py fails.

File Structure

my-package
 | some_module.py
tests
 | conftest.py
 | test_some_module.py.py

conftest.py

I've tried to patch the method by targeting it directly, and by targeting it though the name of the imported module, but neither works and the assertion in test_someclass.py fails.

import pytest
import importlib
module = importlib.import_module('some-package.some_module')
SomeClass = module.SomeClass

@pytest.fixture(scope='function')
def patch_some_module(mocker):

    def some_module():
        return "I'm fake :("

    path1 = 'some-package.some_module.SomeClass.some_method'
    path2 = 'module.SomeClass.some_method'
    mocker.patch(path1, some_module)
    mocker.patch(path2, some_module)

some_module.py

The module in question contains a class with a method that I wish to mock.

class MyClass:

    def my_method(self):
        return "I'm real!"

test_some_module.py

from conftest import SomeClass

def test_some_method():

text = SomeClass().some_method()
expected = "I'm fake :("

assert text == expected

Test Failure

As described above, even with my attempts to patch the some_method method, the test still fails.

================================= test session starts ==================================
platform linux -- Python 3.8.10, pytest-7.0.1, pluggy-1.0.0
rootdir: /home/me/pytest_question
plugins: requests-mock-1.9.3, cov-3.0.0, env-0.6.2, mock-3.7.0
collected 1 item

tests/test_some_module.py F                                                      [100%]

======================================= FAILURES =======================================
___________________________________ test_some_method ___________________________________

    def test_some_method():

        text = SomeClass().some_method()
        expected = "I'm fake :("

>       assert text == expected
E       assert "I'm real!" == "I'm fake :("
E         - I'm fake :(
E           I'm real!

tests/test_some_module.py:8: AssertionError
=============================== short test summary info ================================
FAILED tests/test_some_module.py::test_some_method - assert "I'm real!" == "I'm fake :("
================================== 1 failed in 0.05s ===================================

CodePudding user response:

You don't seem to be using your fixture in tests/test_some_module.py

Try changing it to the following.

# tests/test_some_module.py

from conftest import SomeClass


@pytest.mark.usefixtures("patch_some_module")
def test_some_method():
  text = SomeClass().some_method()
  expected = "I'm fake :("

  assert text == expected

CodePudding user response:

The answer to this question is that the patch has to be applied to the tests.

That can be done either by passing the fixture into selected tests, for example...

from conftest import SomeClass

def test_some_method(patch_some_module):

    text = SomeClass().some_method()
    expected = "I'm fake :("

    assert text == expected

Or by choosing to apply to patch to all tests.

@pytest.fixture(scope='function', autouse=True)
def patch_some_module(mocker):

    def some_module(self):
        return "I'm fake :("

    path = 'some-package.some_module.SomeClass.some_method'
    mocker.patch(path, some_module)
  • Related