7

I would like to understand what is the differance between running:

$ pytest

and

$ python -m pytest tests

I am worikng in a virtual enviroment on a Mac. The first solution gives me an error :

ModuleNotFoundError: No module named 'app'  

The second solution works fine.

Why does the second solution work and the the first one not? How can I make it work with just $ pytest?

My folder structure:

├── app.py   
├── requirements.txt  
├── templates  
├── tests  
│   ├── conftest.py  
│   ├── functional  
│   │   ├── __init__.py  
│   │   └── test_s.py   
│   └── unit   
│       ├── __init__.py   
│       └── test_app.py   
└── venv   

In test_app.py, this line fails:

from app import app
hoefling
  • 59,418
  • 12
  • 147
  • 194
MatyasM
  • 73
  • 4

2 Answers2

6

python -m pytest adds the current directory to sys.path, i.e. lets you import modules from there.

This is a hint that there is something - let's call it - not optimal with your directory structure.

Why this may not be a good thing, you can read in this fantastic blog post

https://blog.ganssle.io/articles/2019/08/test-as-installed.html

I also wrote a short blog post about this topic:

https://jugmac00.github.io/til/what-is-the-difference-between-invoking-pytest-and-python-m-pytest/

Jürgen Gmach
  • 5,366
  • 3
  • 20
  • 37
  • I also think it's worth mentioning for people debugging issues that `pytest` may not necessarily be the same as `python -m pytest`. You should verify that `which pytest` points to a file that is reachable by your python install. e.g. inside the same virtual environment/same python installation. – Leggy Apr 04 '23 at 18:47
1

The difference is comming from the Python algorithm of searching modules, but also the way how Pytest discovers tests.

1. Python module searching

It is done according to Python implementation as following:

  1. search in builtin module
  2. search in sys.path list:
    • first 'current dir' is added to sys.path
    • second PYTHONPATH content is is added to sys.path
    • then in the end all Python installation directories are added to sys.path

It can be quite complicated. I see this nicely explained "in extended mode" here: Where is Python's sys.path initialized from?

2. Pytest discovery mechanism

So

from app import app

when you are importing 'app' like above it will be searched in diffirent way in both cases. In the other words 'current dir' in sys.path is set diffirently for both cases. You can check this by printing sys.path list.

It is not issue, but the way how python/pytest works.

3. Example of workaround

You can workaround this by for example by something like below (I am putting this to separate pathmagic.py file and importing later pathmagic in test_xyz )

import pathmagic  # noqa Pep8 uncheck

pathmagic.py

import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
sys.path.append(os.path.dirname((os.path.dirname(os.path.dirname(os.path.realpath(__file__))))))
Daniel
  • 46
  • 3
  • 1
    No, don't deserve that path magic thing. You should be setting up the environment correctly before running the program using the tools of your operating system – juanpa.arrivillaga Feb 07 '21 at 16:47