Search code examples
pythonpytestpytest-mock

Check if pytest fixture is called once during testing


Does pytest provides functionality like unittest.mock to check if the mock was actually called once(or once with some parameter)?

Sample Source code:

my_package/my_module.py

from com.abc.validation import Validation


class MyModule:
    def __init__(self):
        pass

    def will_call_other_package(self):
        val = Validation()
        val.do()

    def run(self):
        self.will_call_other_package()

Sample test code for the above source code:

test_my_module.py

import pytest
from pytest_mock import mocker

from my_package.my_module import MyModule

@pytest.fixture
def mock_will_call_other_package(mocker):
    mocker.patch('my_package.my_module.will_call_other_package')


@pytest.mark.usefixtures("mock_will_call_other_package")
class TestMyModule:

    def test_run(self):
        MyModule().run()
        #check `will_call_other_package` method is called.

        #Looking for something similar to what unittest.mock provide
        #mock_will_call_other_package.called_once


Solution

  • If you want to use a fixture that does the patching, you can move the patching into a fixture:

    import pytest
    from unittest import mock
    
    from my_package.my_module import MyModule
    
    @pytest.fixture
    def mock_will_call_other_package():
        with mock.patch('my_package.my_module.will_call_other_package') as mocked:
            yield mocked
        # the mocking will be reverted here, e.g. after the test
    
    
    class TestMyModule:
    
        def test_run(self, mock_will_call_other_package):
            MyModule().run()
            mock_will_call_other_package.assert_called_once()
    

    Note that you have to use the fixture parameter in the test. Just using @pytest.mark.usefixtures will not give you access to the mock itself. You can still use it to be effective in all tests in the class, if you don't need to access the mock in all tests (or use autouse=True in the fixture).

    Also note that you don't need pytest-mock here - but as mentioned by @hoefling, using it makes the fixture better readable, because you don't need the with clause :

    @pytest.fixture
    def mock_will_call_other_package(mocker):
        yield mocker.patch('my_package.my_module.will_call_other_package')
    

    As an aside: you don't need to import mocker. Fixtures are looked up by name, and available automatically if the respective plugin is installed.