I am working on a package in Python. I use virtualenv. I set the path to the root of the module in a .pth path in my virtualenv, so that I can import modules of the package while developing the code and do testing (Question 1: is it a good way to do?). This works fine (here is an example, this is the behavior I want):
I am a bit puzzled, it looks like this indicates an import error, but Python does it fine so why is there a problem specifically with PyTest? Any suggestion to the reason / remedy (Question 2)? I googled and stack-overflowed the 'ImportError: cannot import' error for PyTest, but the hits I got were related to missing python path and remedy to this, which does not seem to be the problem here. Any suggestions?
Simply put an empty conftest.py file in the project root directory, because when pytest discovers a conftest.py, it modifies sys.path so it can import stuff from the conftest module.A general directory structure can be:
I originally answered this in October 2021. I had struggled with this problem for ages. As described in my original answer below NONE of the solutions here worked. Over 2 years I got 24 upvotes (and 2 downvotes) so it seemed like others were having similar intractable difficulties. I had reluctantly concluded that I had to manipulate sys.path to get pytest to be able to "see" the files that I thought it should see.
I was wrong and failed to understand something about paths. And CRUCIALLY this involves the running of a project (as most of my projects are) where the application is run by using this command (in W10):
This is where the problem arose. If you are not in fact running a file in the root directory of your project you need to be aware that putting the tests directory in your root directory will plunge you into a world of pain and bafflement: sibling .py files will bafflingly not be "found" in your main "run" directory, i.e. where CWD is, i.e. by virtue of your command line.
I explored this question and various others on SO and elsewhere... all the stuff about adding (or removing) an empty __init__.py in and/or conftest.py in various parts of the project directory structure, all the stuff about PYTHONPATH, etc., etc.: NONE of these solutions worked for me, in what is actually a very simple situation, and which shouldn't be causing any grief.
I regard this as a flaw in pytest's current setup. In fact I got a message recently from someone on SO who clearly knew his stuff. He said that pytest is not designed to work with (as per Java/Groovy/Gradle) separate "src" and "test" directory structures, and that test files should be mingled in amongst the application directories and files. This perhaps goes some way to providing an explanation ... however, tests, particularly integration/functional tests, don't always necessarily correspond neatly to particular directories, and I think pytest should give users more choice in this regard.
on the line in __main__.py that tries to import "my_other_file". Note that the problem here, in my case, is that, in the course of a pytest test, one application file fails to find another application file in the same package.
USING PYTHONPATH
After a lot of experimentation, and putting an __init__.py file and a confest.py file in just about every directory I could find (I think the crucial files were __init__.py added to "tests" and "basic_tests", see above directory structure), and then setting PYTHONPATH to as follows
... I found it worked. Imports in the testing files had to be tweaked a bit, the general pattern being from core import app, project, but the test files were able to "see" core, and crucially there was no need to mess around with the import commands in the app files.
HOWEVER... for some reason the tests now run much slower using this method! Compared to my solution below, where the core package can be seen to be loaded just once, my suspicion is that the PYTHONPATH solution probably results in vast amounts of code being reloaded again and again. I can't yet confirm this, but I can't see any other explanation.
As I say, I regard this as a flaw in pytest's setup. I have absolutely no idea what pytest does to establish a common-sense setup for sys.path, but it's obviously getting it wrong. There should be no need to rely on PYTHONPATH, or whatever, and if indeed this is the "official" solution, the documentation on this subject is sorely lacking.
The ONLY working solution, at least on my machines (OS W10 and Linux, as at pytest 7.3.1), is to add the application packages to sys.path. It is undesirable to mess with sys.path, but in this case nothing else at all works for me.
The most obvious place to do this is conftest.py, which gets executed at the start of each pytest run, and which is kept the project root directory. It involves no modification of the app files. Typically (for the above directory/files setup):
I am currently (2023-08) using pytest v 7.3.1, and have also tried editing pytest.ini, as suggested by ian's 2022 answer, where it is suggested that these configurations in pytest.ini are a new thing to solve the problem since v7 (release 2021-12). In my machines this does not solve the problem: without manually altering sys.path pytest's attempt to import __main__.py in my example fails as before: because import my_other_file raises ModuleNotFoundError: No module named 'my_other_file'.
PS I have now set up a minimal git repo illustrating the problem: download the .zip at _probs/tree/main, and read what I say in README.md. Interested to know if anyone gets different results to what I get.
I had a similar issue, exact same error, but different cause. I was running the test code just fine, but against an old version of the module. In the previous version of my code one class existed, while the other did not. After updating my code, I should have run the following to install it.
In my case, the import error occurred because the package is pointing to another package/directory with the same name and its path is one level above the folder I actually wanted.I think this also explains why some people need to remove _ init _.py while others need to add back.
Run an experiment where you print sys.path when running the code normally.Then print sys.path while running the code via pytest. I think you will find that there is a difference between these two paths, hence why pytest breaks.
I had the problem using tox. So my program ran fine, but unittests via tox kept complaining.After installing packages (needed for the program) you need to additionally specify the packages used in the unittests in the tox.ini
I could find no way of getting this to work from VS Code because the src folder just blew the mind of the import system. I can imagine there is a way of getting this to work from the command line. As a relatively new convert to Python programming it gives me a nostalgic feeling of working with COM, but being slightly less fun.
Each test here is being given its own copy of that list object,which means the order fixture is getting executed twice (the sameis true for the first_entry fixture). If we were to do this by hand aswell, it would look something like this:
If a requested fixture was executed once for every time it was requestedduring a test, then this test would fail because both append_first andtest_string_only would see order as an empty list (i.e. []), butsince the return value of order was cached (along with any side effectsexecuting it may have had) after the first time it was called, both the test andappend_first were referencing the same object, and the test saw the effectappend_first had on that object.
Fixtures requiring network access depend on connectivity and areusually time-expensive to create. Extending the previous example, wecan add a scope="module" parameter to t...@pytest.fixture invocationto cause a smtp_connection fixture function, responsible to create a connection to a preexisting SMTP server, to only be invokedonce per test module (the default is to invoke once per test function).Multiple test functions in a test module will thuseach receive the same smtp_connection fixture instance, thus saving time.Possible values for scope are: function, class, module, package or session.
You see the two assert 0 failing and more importantly you can also seethat the exactly same smtp_connection object was passed into thetwo test functions because pytest shows the incoming argument values in thetraceback. As a result, the two test functions using smtp_connection runas quick as a single one because they reuse the same instance.
In some cases, you might want to change the scope of the fixture without changing the code.To do that, pass a callable to scope. The callable must return a string with a valid scopeand will be executed only once - during the fixture definition. It will be called with twokeyword arguments - fixture_name as a string and config with a configuration object.
This can be especially useful when dealing with fixtures that need time for setup, like spawninga docker container. You can use the command-line argument to control the scope of the spawnedcontainers for different environments. See the example below.
In order to use this approach, we have to request the request-context object(just like we would request another fixture) in the fixture we need to addteardown code for, and then pass a callable, containing that teardown code, toits addfinalizer method.
The safest and simplest fixture structure requires limiting fixtures to onlymaking one state-changing action each, and then bundling them together withtheir teardown code, as the email examples above showed.
The chance that a state-changing operation can fail but still modify state isnegligible, as most of these operations tend to be transaction-based (at least at thelevel of testing where state could be left behind). So if we make sure that anysuccessful state-changing action gets torn down by moving it to a separatefixture function and separating it from other, potentially failingstate-changing actions, then our tests will stand the best chance at leavingthe test environment the way they found it.
c80f0f1006