Differance between $ pytest and $ python -m pytest tests

Viewed 756

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
2 Answers

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__))))))
Related