question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Pytest plugin has side effect and is extremely unintuitive to use

See original GitHub issue

This 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 fs parameter 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 fs fixtures in different tests. The pytest fixture that pyfakefs registers is global state.
  • I cannot use the @patchfs decorator on one of my pytest unit tests to make the pyfakefs dependency more obvious as @patchfs uses the same named parameter fs and employs functools.wraps to make the wrapped function look like the original one (including its named parameters). (Attempting to use @patchfs on 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_plugin in 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 @patchfs and using fixtures is essentially global state. (This would be a breaking change, too.)
  • At least make @patchfs pass the FakeFilesystem instance to the decorated function as the first parameter, instead of as a named parameter fs. This way, one could still use @patchfs on 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:closed
  • Created 3 years ago
  • Comments:9 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
mrbean-bremencommented, Nov 18, 2020

Yes, but in reality it is vice verse now. patch goes through some hops to make it the the other (expected) way around, but only if you have several patch decorators. They basically collect all patch decorators, 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.

1reaction
mrbean-bremencommented, Nov 9, 2020

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 @patchfs before @mock.patch will put the fake fs argument before the mock argument instead of after it). This is due to some magic that mock does–I will try to wrap my head around this, but that may take a while.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found