简体   繁体   中英

How to elegantly mock a class using pytest fixtures?

Let's say I have a fixture to mock a class using monkeypatch .

# conftest.py
@pytest.fixture
def mock_dummyclass(monkeypatch):
    def mock_function():
        return None
 
    monkeypatch.setattr(dummypackage, "dummyclass", mock_function)

Now, I have to use this fixture in my test so that this class is mocked.

#test_dummy.py
@pytest.mark.usefixtures("mock_dummyclass")
class TestManyDummyMethods:
    def test_dummy_one():
        import dummypackage  # Flag A
    def test_dummy_two():
        import dummypackage  # Flag B
    ...
    def test_dummy_n():
        import dummypackage  # Flag N

As you can see in the flagged lines, that I'll have to import this module dummypackage inside every function to ensure that the fixture is applied before the module is imported (otherwise the fixture would not be effective).

Importing dummypackage inside the class would work but calling self.dummypackage inside functions doesn't seem very elegant either.

Is there a more elegant way to achieve this?

Comment: The monkeypatch library doesn't seem to be maintained anymore. unittest.mock should probably offer all you need.

I would try to avoid a solution that depends on importing the module as part of your test because that would break if it was imported for another reason (eg import other functions).

I am using os as an example as that exists and makes it reproducible.

How best to patch seem to depend on how you import from another module.

Example 1: target_module1.py (importing the join method from os.path ):

from os.path import join

def some_method_using_join():
    return join('parent', 'child')

This requires us to patch the join method of target_module1.py :

target_module1_test.py :

from unittest.mock import patch, MagicMock

import pytest

from target_module1 import some_method_using_join

@patch('target_module1.join')
def some_test(join_mock: MagicMock):
    some_method_using_join()

Example 2: target_module2.py (importing the os module):

import os

def some_method_using_join():
    return os.path.join('parent', 'child')

This allows us to patch the join method on os.path :

target_module2_test.py :

from unittest.mock import patch, MagicMock

import pytest

from target_module2 import some_method_using_join

@patch('os.path.join')
def some_test(join_mock: MagicMock):
    some_method_using_join()

This assumes that you don't need to patch a class or method that is used at the module level (ie while importing).

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