Reputation: 18128
EDIT: While any testing recommendations are truly appreciated, I'm wondering specifically if there'a way that pytest can enforce the isolation for me, rather than relying on myself always remembering to clean up mocks.
Does pytest
support "resetting" process state between individual Python files, or otherwise isolating test files from one another?
Our CI used to invoke pytest on individual files, like this:
pytest file1.py
pytest file2.py
...
It now invokes pytest once for all files, like this:
pytest file1.py file2.py ...
We ran into trouble when some test files (e.g. file1.py
) performed module-level mocks. For example (simplified):
def setup_module():
patch("path.to.some.module.var", Mock()).start()
(There is no corresponding teardown_module
.)
This works fine when pytest is called on files one-at-a-time. But when it runs against multiple files, any mocks made by previously executed test code persist into subsequent test code. Is there any way to "reset" this state between files? E.g., does pytest support a way to invoke each file's tests in a separate process? (Looked at the pytest docs but found nothing like this.)
For now, we're adding teardown_module()
functions that call patch.stopall()
, but it would be nice to have the added security of pytest implicitly isolating our test files from one another.
Upvotes: 4
Views: 2861
Reputation: 16815
The standard way to do this is probably to use a fixture with a context manager instead of the setup/teardown construct:
@pytest.fixture(scope="module", autouse=True)
def patch_var():
with mock.patch("path.to.some.module.var"):
yield
This will end the patching after the test goes out of module scope.
It has the same effect as the less convenient:
@pytest.fixture(scope="module", autouse=True)
def patch_var():
patcher = mock.patch("path.to.some.module.var")
patcher.start()
yield
patcher.stop()
Note that you are responsible to stop patching yourself if you use start
on the constructed patcher object. It is always more secure to use the context manager or the patch decorator (if applied to a single test) for patching.
UPDATE:
As far as I know, there is no way to unconditionally isolate the test modules completely from one another if executed in the same test run. This is what the concept of fixture scopes is for.
A fixture shall always be written with automatic cleanup. For patching, you use the context manager (as shown above) which does the cleanup for you, other things you have to cleanup yourself after the yield
. If you want to have global changes over the whole test run, you use session-scoped fixtures. If you want to isolate test modules, you use module-scoped fixtures, and for test class or single test isolation you use class- or function-scoped fixtures.
Upvotes: 5