PATH issue with pytest 'ImportError: No module named YadaYadaYada'

PythonUnit TestingPytest

Python Problem Overview


I used easy_install to install pytest on a mac and started writing tests for a project with a file structure likes so:

repo/
   |--app.py
   |--settings.py
   |--models.py
   |--tests/
          |--test_app.py

run py.test while in the repo directory, everything behaves as you would expect

but when I try that same thing on either linux or windows (both have pytest 2.2.3 on them) it barks whenever it hits its first import of something from my application path. Say for instance from app import some_def_in_app

Do I need to be editing my PATH to run py.test on these systems? Has Anyone experienced this?

Python Solutions


Solution 1 - Python

I'm not sure why py.test does not add the current directory in the PYTHONPATH itself, but here's a workaround (to be executed from the root of your repository):

python -m pytest tests/

It works because Python adds the current directory in the PYTHONPATH for you.

Solution 2 - Python

Recently, pytest has added a new core plugin that supports sys.path modifications via the pythonpath configuration value. The solution is thus much simpler now and doesn't require any workarounds anymore:

pyproject.toml example:

[tool.pytest.ini_options]
pythonpath = [
  "."
]

pytest.ini example:

[pytest]
pythonpath = .

The path entries are calculated relative to the rootdir, thus . adds repo directory to sys.path in this case.

Multiple path entries are also allowed: for a layout

repo/
├── src/
|   └── lib.py
├── app.py
└── tests
     ├── test_app.py
     └── test_lib.py

the configuration

[tool.pytest.ini_options]
pythonpath = [
  ".", "src",
]

or

[pytest]
pythonpath = . src

will add both app and lib modules to sys.path, so

import app
import lib

will both work.

The least invasive solution is adding an empty file named conftest.py in the repo/ directory:

$ touch repo/conftest.py

That's it. No need to write custom code for mangling the sys.path or remember to drag PYTHONPATH along, or placing __init__.py into dirs where it doesn't belong (using python -m pytest as suggested in Apteryx's answer is a good solution though!).

The project directory afterwards:

repo
├── conftest.py
├── app.py
├── settings.py
├── models.py
└── tests
     └── test_app.py
Explanation

pytest looks for the conftest modules on test collection to gather custom hooks and fixtures, and in order to import the custom objects from them, pytest adds the parent directory of the conftest.py to the sys.path (in this case the repo directory).

Other project structures

If you have other project structure, place the conftest.py in the package root dir (the one that contains packages but is not a package itself, so does not contain an __init__.py), for example:

repo
├── conftest.py
├── spam
│   ├── __init__.py
│   ├── bacon.py
│   └── egg.py
├── eggs
│   ├── __init__.py
│   └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py
src layout

Although this approach can be used with the src layout (place conftest.py in the src dir):

repo
├── src
│   ├── conftest.py
│   ├── spam
│   │   ├── __init__.py
│   │   ├── bacon.py
│   │   └── egg.py
│   └── eggs 
│       ├── __init__.py
│       └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

beware that adding src to PYTHONPATH mitigates the meaning and benefits of the src layout! You will end up with testing the code from repository and not the installed package. If you need to do it, maybe you don't need the src dir at all.

Where to go from here

Of course, conftest modules are not just some files to help the source code discovery; it's where all the project-specific enhancements of the pytest framework and the customization of your test suite happen. pytest has a lot of information on conftest modules scattered throughout their docs; start with conftest.py: local per-directory plugins

Also, SO has an excellent question on conftest modules: In py.test, what is the use of conftest.py files?

Solution 3 - Python

I had the same problem. I fixed it by adding an empty __init__.py file to my tests directory.

Solution 4 - Python

Yes, the source folder is not in Python's path if you cd to the tests directory.

You have 2 choices:

  1. Add the path manually to the test files, something like this:

     import sys, os
     myPath = os.path.dirname(os.path.abspath(__file__))
     sys.path.insert(0, myPath + '/../')
    
  2. Run the tests with the env var PYTHONPATH=../.

Solution 5 - Python

Run pytest itself as a module with: python -m pytest tests

Solution 6 - Python

You can run with PYTHONPATH in project root

PYTHONPATH=. py.test

Or use pip install as editable import

pip install -e .   # install package using setup.py in editable mode

Solution 7 - Python

I had the same problem in Flask.

When I added:

__init__.py

to tests folder, problem disappeared :)

Probably application couldn't recognize folder tests as module

Solution 8 - Python

I created this as an answer to your question and my own confusion. I hope it helps. Pay attention to PYTHONPATH in both the py.test command line and in the tox.ini.

https://github.com/jeffmacdonald/pytest_test

Specifically: You have to tell py.test and tox where to find the modules you are including.

With py.test you can do this:

PYTHONPATH=. py.test

And with tox, add this to your tox.ini:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}

Solution 9 - Python

I fixed it by removing the top-level __init__.py in the parent folder of my sources.

Solution 10 - Python

I started getting weird ConftestImportFailure: ImportError('No module named ... errors when I had accidentally added __init__.py file to my src directory (which was not supposed to be a Python package, just a container of all source).

Solution 11 - Python

I was having the same problem when following the Flask tutorial and I found the answer on the official Pytest docs It's a little shift from the way I (and I think many others) are used to do things.

You have to create a setup.py file in your project's root directory with at least the following two lines:

from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())

where PACKAGENAME is your app's name. Then you have to install it with pip:

pip install -e .

The -e flag tells pip to intall the package in editable or "develop" mode. So the next time you run pytest it should find your app in the standard PYTHONPATH.

Solution 12 - Python

I had a similar issue. pytest did not recognize a module installed in the environment I was working in.

I resolved it by also installing pytest into the same environment.

Solution 13 - Python

Also if you run pytest within your virtual environment make sure pytest module is installed within your virtual environment. Activate your virtual env and run pip install pytest.

Solution 14 - Python

It is a bit of a shame that this is an issue in python... But just add this environment variable is the most comfortable way IMO:

export PYTHONPATH=$PYTHONPATH:.

You can use direnv for this to happen automatically :)

Solution 15 - Python

For me the problem was tests.py generated by Django along with tests directory. Removing tests.py solved the problem.

Solution 16 - Python

I got this error as I used relative imports incorrectly. In the OP example, test_app.py should import functions using e.g.

from repo.app import *

However liberally _init_.py files are scattered around the file structure, this does not work and creates the kind of ImportError seen unless the files and test files are in the same directory.

from app import *

Here's an example of what I had to do with one of my projects:

Here’s my project structure:

microbit/
microbit/activity_indicator/activity_indicator.py
microbit/tests/test_activity_indicator.py

To be able to access activity_indicator.py from test_activity_indicator.py I needed to:

  • start test_activity_indicatory.py with the correct relative import:
    from microbit.activity_indicator.activity_indicator import *
  • put _init_.py files throughout the project structure:
    microbit/
    microbit/__init__.py
    microbit/activity_indicator/__init__.py
    microbit/activity_indicator/activity_indicator.py
    microbit/tests/__init__.py
    microbit/tests/test_activity_indicator.py

Solution 17 - Python

I was getting this error due to something even simpler (you could even say trivial). I hadn't installed the pytest module. So a simple apt install python-pytest fixed it for me.

'pytest' would have been listed in setup.py as a test dependency. Make sure you install the test requirements as well.

Solution 18 - Python

Since no one has suggested it, you could also pass the path to the tests in your pytest.ini file:

[pytest]
...
testpaths = repo/tests

see documentation: https://docs.pytest.org/en/6.2.x/customize.html#pytest-ini

Side effect for VScode: it should pick up the unit test in the UI.

Solution 19 - Python

According to a post on Medium by Dirk Avery (and supported by my personal experience) if you're using a virtual environment for your project then you can't use a system-wide install of pytest; you have to install it in the virtual environment and use that install.

In particular, if you have it installed in both places then simply running the pytest command won't work because it will be using the system install. As the other answers have described, one simple solution is to run python -m pytest instead of pytest; this works because it uses the environment's version of pytest. Alternatively, you can just uninstall the system's version of pytest; after reactivating the virtual environment the pytest command should work.

Solution 20 - Python

As pointed out by Luiz Lezcano Arialdi, the correct solution is to install your package as an editable package.

Since I am using pipenv, I thought about adding to his answer a step-by-step how to install the current path as an edible with pipenv, allowing to run pytest without the need of any mangling code or loose files.

You will need to have the following minimal folder structure (documentation):

package/
    package/
        __init__.py
        module.py
    tests/
        module_test.py
    setup.py

setup.py most have the following minium code (documentation):

import setuptools


setuptools.setup(name='package', # Change to your package name
                 packages=setuptools.find_packages())

Then you just need to run pipenv install --dev -e . and pipenv will install the current path as an editable package (the --dev flag is optional) (documentation).

Now you shoul be able to run pytest without problems.

Solution 21 - Python

We have fixed the issue by adding the following environment variable.

PYTHONPATH=${PYTHONPATH}:${PWD}/src:${PWD}/test

Solution 22 - Python

If this pytest error appears not for your own package, but for a git-installed package in your package's requirements.txt, the solution is to switch to editable installation mode.

For example, suppose your package's requirements.txt had the following line:

git+https://github.com/foo/bar.git

You would instead replace it with the following:

-e git+https://github.com/foo/bar.git#egg=bar

Solution 23 - Python

Very often the tests were interrupted due to module being unable to be imported,After research, I found out that the system is looking at the file in the wrong place and we can easily overcome the problem by copying the file, containing the module, in the same folder as stated, in order to be properly imported. Another solution proposal would be to change the declaration for the import and show MutPy the correct path of the unit. However, due to the fact that multiple units can have this dependency, meaning we need to commit changes also in their declarations, we prefer to simply move the unit to the folder.

Solution 24 - Python

My solution:

create the conftest.py file in the test directory containing:

import os
import sys
sys.path.insert(0,os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/code/")

This will add the folder of interest to the python path without modifying every test file, setting env variable or messing with absolute/relative paths.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMattoToddView Question on Stackoverflow
Solution 1 - PythonApteryxView Answer on Stackoverflow
Solution 2 - PythonhoeflingView Answer on Stackoverflow
Solution 3 - PythonAron CurzonView Answer on Stackoverflow
Solution 4 - PythonNot_a_GolferView Answer on Stackoverflow
Solution 5 - PythonStefano MessinaView Answer on Stackoverflow
Solution 6 - PythonFord GuoView Answer on Stackoverflow
Solution 7 - Pythonuser13037517View Answer on Stackoverflow
Solution 8 - PythonJeff MacDonaldView Answer on Stackoverflow
Solution 9 - PythonGonzaloView Answer on Stackoverflow
Solution 10 - PythonjbaskoView Answer on Stackoverflow
Solution 11 - PythonLuis Lezcano AiraldiView Answer on Stackoverflow
Solution 12 - PythonnocibambiView Answer on Stackoverflow
Solution 13 - PythonAlirezaView Answer on Stackoverflow
Solution 14 - PythontbrodbeckView Answer on Stackoverflow
Solution 15 - PythonPaweł MuchaView Answer on Stackoverflow
Solution 16 - PythonOppyView Answer on Stackoverflow
Solution 17 - PythoncraqView Answer on Stackoverflow
Solution 18 - PythonthorocView Answer on Stackoverflow
Solution 19 - PythonEinhaenderView Answer on Stackoverflow
Solution 20 - PythonBruno CavalleriView Answer on Stackoverflow
Solution 21 - PythonSANN3View Answer on Stackoverflow
Solution 22 - PythonBetterthan KworaView Answer on Stackoverflow
Solution 23 - PythonBaydaaView Answer on Stackoverflow
Solution 24 - Pythondavide burbaView Answer on Stackoverflow