8

I want to use pytest to do unit testing for the scripts in another folder called src. Here is my directory structure:

src      
  __init__.py
  script1.py
  script2.py
test
  test_fun.py

However, when I try to run pytest in the test folder through command line, I saw the following error

from script2 import fun2
E ModuleNotFoundError: No module named 'script2'

In each script, I have the following contents

script2.py:

def fun2():
return('result_from_2')

script1.py:

from script2 import fun2

def fun1():
    return(fun2()+'_plus_something')

test_fun.py:

import sys
sys.path.append('../')
from src.script1 import fun1

def test_fun1():        
    output1 = fun1()

    # check output
    assert output1=='result_from_2_plus_something'

How can I run pytest with the directory provided above?

Johnny Chiu
  • 459
  • 6
  • 12

4 Answers4

4

When importing a file, Python only searches the current directory, the directory that the entry-point script is running from and sys.path.

Modify test_fun.py as follows:

import sys
sys.path.insert(0, '../src/')
from script1 import fun1

def test_fun1():        
    output1 = fun1()

    # check output
    assert output1=='result_from_2_plus_something'
Venkatesh Wadawadagi
  • 2,793
  • 21
  • 34
2

When we run pytest, the python uses the sys.path to look for the modules in order to resolve the imports. So, if your tests are in the path: "/Users/YourName/projectName/test" and you need to import some modules from "/Users/YourName/projectName/src" Python won't be able to find them.

Then why Python can find 'pytest' or 'sys'? Because they are in other paths existents in sys.path.

To verify what are the existents paths, use:

  import pprint
  import sys
  pprint.pprint(sys.path)

You will see a list of possible paths that will be used by python to resolve the imports:

  ['/Users/yourName/YourProject/test',
 '/opt/anaconda3/bin',
 '/opt/anaconda3/lib/python37.zip',
 '/opt/anaconda3/lib/python3.7',
 '/opt/anaconda3/lib/python3.7/lib-dynload',
 '/opt/anaconda3/lib/python3.7/site-packages',
 '/opt/anaconda3/lib/python3.7/site-packages/aeosa']

Notice that list doesn't content the "/Users/YourName/projectName/src". That's why you need to use the following command in you test_fun.py file to add the 'src' path in this list.

 import sys
 sys.path.append('/Users/YourName/projectName/src').
 from src.script1 import fun1

After this command, the list will be like this:

 ['/Users/yourName/YourProject/test',
 '/opt/anaconda3/bin',
 '/opt/anaconda3/lib/python37.zip',
 '/opt/anaconda3/lib/python3.7',
 '/opt/anaconda3/lib/python3.7/lib-dynload',
 '/opt/anaconda3/lib/python3.7/site-packages',
 '/opt/anaconda3/lib/python3.7/site-packages/aeosa',
 '/Users/yourName/YourProject/src']

Then the Python will be able to resolve the import of your Classes.

2

A good way to do this is following the recommendations in Pytest docs, combined with the third way given in this answer here. This implies:

  1. Creating a simple config.py for your project. Make sure to configure packages and package_dir correctly. Read more here.
  2. Install an "editable" it using pip install -e . This will create .egg.info in your project folder and also install it.
  3. Test it using pytest.
  4. Delete that "editable" package using pip uninstall name_of_package and then removing the egg with rm -r $(find . -name '*.egg-info')

I know it seems more complex than the simple hacks that simply change sys.path but it is a more clean approach. One thing that was hard for me to understand at the beginning was that using this method you are effectively installing the package to do testing. I hadn't done that with other languages. The reason:

you can think of it as a way to also test the installer itself. Without this step, it's completely possible to have a passing test suite and yet have a broken installer (and you end up to accidentally publish a broken package)

(from the discussion of this answer here)

nonameable
  • 426
  • 3
  • 11
  • Also ensure pytest is installed in your venv. Else a system venv gets used (if installed) which will not find the modules from your venv. – MKesper Jun 15 '23 at 09:45
2

Try running

python -m pytest 

I don't know why but this worked for me without changing the code in any of the files

faiqali
  • 21
  • 1
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 17 '21 at 09:12