I would suggest injecting mocks using pytest
fixtures instead of manual mock copying. The function-scoped fixtures (the default ones) reevaluate for each test. Example: assume you have a module
# mod.py
def spam():
return eggs()
def eggs():
return "eggs"
and a test
from unittest.mock import patch
from mod import spam
@patch("mod.eggs")
def test_bacon(mock1):
mock1.return_value = "bacon"
assert spam() == "bacon"
and now you want to refactor it into testing against bacon
and bacon with eggs
. Move out the patching inside a fixture:
import pytest
from unittest.mock import patch
from mod import spam
@pytest.fixture
def eggs_mock():
with patch("mod.eggs") as mock1:
yield mock1
def test_bacon(eggs_mock):
eggs_mock.return_value = "bacon"
assert spam() == "bacon"
def test_bacon_with_eggs(eggs_mock):
eggs_mock.return_value = "bacon with eggs"
assert spam() == "bacon with eggs"
You now have two different mocks of the mod.eggs
function, one unique mock in each test.
unittest
-style tests
This approach also works with unittest
test classes, although the injection is a bit more verbose since unittest.TestCase
s don't accept arguments in test methods. This is the same approach as described in this answer of mine. In the example below, I store the eggs_mock
fixture return value in a Tests
instance attribute via using an additional autouse fixture:
from unittest import TestCase
from unittest.mock import patch
import pytest
from mod import spam
@pytest.fixture
def eggs_mock():
with patch("mod.eggs") as mock1:
yield mock1
class Tests(TestCase):
@pytest.fixture(autouse=True)
def inject_eggs_mock(self, eggs_mock):
self._eggs_mock = eggs_mock
def test_bacon(self):
self._eggs_mock.return_value = "bacon"
assert spam() == "bacon"
def test_bacon_with_eggs(self):
self._eggs_mock.return_value = "bacon with eggs"
assert spam() == "bacon with eggs"