Pytest plugin has side effect and is extremely unintuitive to use
See original GitHub issueThis might not be considered a bug, but it is – at the very least – a severe code smell.
Describe the bug Once pyfakefs is installed, the pytest plugin seems to get loaded automatically and register a fixture with pytest. This has severe consequences:
- I don’t even have to import pyfakefs anywhere in my project’s code and yet I still have a dependency on pyfakefs that is completely non-obvious.
- It is hard to read someone else’s unit tests, as it is unclear where that
fsparameter is coming from - If I try to make the dependency on pyfakefs obvious by e.g. doing
from pyfakefs.pytest_plugin import fs, the import will seem un-used and linters and code formatters will complain about it or even remove it automatically. - I cannot have different
fsfixtures in different tests. The pytest fixture that pyfakefs registers is global state. - I cannot use the
@patchfsdecorator on one of my pytest unit tests to make the pyfakefs dependency more obvious as@patchfsuses the same named parameterfsand employsfunctools.wrapsto make the wrapped function look like the original one (including its named parameters). (Attempting to use@patchfson a unit test results in a recursion error. Let me know if I should file a separate bug report for this.)
I am aware that a large part of this issue is caused by pytest’s weird approach of having a global registry of fixtures. However, the fixture in pyfakefs exacerbates this by orders of magnitude.
Suggestions:
- Wrap the pytest fixture in
pyfakefs.pytest_pluginin a function that the user has to call explicitly somewhere in her/his code in order to register the fixture. (Clearly, this would be a breaking change.) - As an alternative, get rid of the fixture – the same functionality could be obtained by using
@patchfsand using fixtures is essentially global state. (This would be a breaking change, too.) - At least make
@patchfspass the FakeFilesystem instance to the decorated function as the first parameter, instead of as a named parameterfs. This way, one could still use@patchfson pytest unit tests without interfering with the fixture. (This could break some existing code.) - At the very least: Document this very strange behavior. It even took me a while to understand that the example in the current docs is not just a code snippet that’s missing setup code and imports but it’s all the code that’s needed. It is uncompletely unclear that the pytest plugin that’s mentioned in the docs gets loaded automatically and the fixture will be registered by default. In fact, in order to learn all this I had to dig through pyfakefs’s code.
While a breaking change seems little desirable, it is IMO the only real solution in the long term. One could try to smoothen the transition process by e.g. deprecating the fixture first.
Issue Analytics
- State:
- Created 3 years ago
- Comments:9 (6 by maintainers)
Top Results From Across the Web
Pytest plugin has side effect and is extremely unintuitive to ...
Once pyfakefs is installed, the pytest plugin seems to get loaded automatically and register a fixture with pytest. This has severe consequences: I...
Read more >Pytest plugin has side effect and is extremely unintuitive to use
Once pyfakefs is installed, the pytest plugin seems to get loaded automatically and register a fixture with pytest. This has severe consequences: I...
Read more >Changelog — pytest documentation
#10344: Update information on writing plugins to use pyproject.toml instead ... This has the side effect that pytest.deprecated_call now raises pytest.fail.
Read more >pytest-timeout
See below for detailed information on the timeout methods and their side-effects. The pytest-timeout plugin has been tested on Python 3.6 and higher, ......
Read more >Welcome to Pytest-BDD's documentation! - Read the Docs
This allows to have single implementation and multiple use, so less code. ... are executed imperatively to apply possible side-effects, pytest-bdd is trying ......
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

Yes, but in reality it is vice verse now.
patchgoes through some hops to make it the the other (expected) way around, but only if you have severalpatchdecorators. They basically collect allpatchdecorators, and add the arguments in the reverse order afterwards. This does not play nice with other decorators who want to do the same, and I gave up on this and just documented the behavior for now.I will try to change the keyword argument to a positional argument, as this would certainly be better. As for the pytest plugin behavior–I may think about it, and will see what @jmcgeheeiv thinks, but I currently don’t see much benefit in changing it.
I now remember why I didn’t use a positional argument in the first place. While it is very easy to change, it does not play well with
mock.patch, because the order of the expected arguments is not what you might expect (e.g. writing@patchfsbefore@mock.patchwill put the fake fs argument before the mock argument instead of after it). This is due to some magic thatmockdoes–I will try to wrap my head around this, but that may take a while.