40

I have some Python package and some tests. The files are layed out following http://pytest.org/latest/goodpractices.html#choosing-a-test-layout-import-rules

Putting tests into an extra directory outside your actual application code, useful if you have many functional tests or for other reasons want to keep tests separate from actual application code (often a good idea):

setup.py   # your distutils/setuptools Python package metadata
mypkg/
    __init__.py
    appmodule.py
tests/
        test_app.py

My problem is, when I run the tests py.test, I get an error

ImportError: No module named 'mypkg'

I can solve this by installing the package python setup.py install but this means the tests run against the installed package, not the local one, which makes development very tedious. Whenever I make a change and want to run the tests, I need to reinstall, else I am testing the old code.

What can I do?

imz -- Ivan Zakharyaschev
  • 4,921
  • 6
  • 53
  • 104
Colonel Panic
  • 132,665
  • 89
  • 401
  • 465

4 Answers4

25

I know this question has been already closed, but a simple way I often use is to call pytest via python -m, from the root (the parent of the package).

$ python -m pytest tests

This works because -m option adds the current directory to the python path, and hence mypkg is detected as a local package (not as the installed).

See: https://docs.pytest.org/en/latest/usage.html#calling-pytest-through-python-m-pytest

Kota Mori
  • 6,510
  • 1
  • 21
  • 25
17

The normal approach for development is to use a virtualenv and use pip install -e . in the virtualenv (this is almost equivalent to python setup.py develop). Now your source directory is used as installed package on sys.path.

There are of course a bunch of other ways to get your package on sys.path for testing, see Ensuring py.test includes the application directory in sys.path for a question with a more complete answer for this exact same problem.

flub
  • 5,953
  • 27
  • 24
1

On my side, while developing, I prefer to run tests from the IDE (using a runner extension) rather than using the command line. However, before pushing my code or prior to a release, I like to use the command line.

Here is a way to deal with this issue, allowing you to run tests from both the test runner used by your IDE and the command line.

My setup:

  • IDE: Visual Studio Code

  • Testing: pytest

  • Extension (test runner): https://marketplace.visualstudio.com/items?itemName=LittleFoxTeam.vscode-python-test-adapter

  • Work directory structure (my solution should be easily adaptable to your context):

    project_folder/
    
        src/
            mypkg/
                __init__.py
                appmodule.py
        tests/
             mypkg/
                appmodule_test.py
        pytest.ini           <- Use so pytest can locate pkgs from ./src 
        .env                 <- Use so VsCode and its extention can locate pkgs from ./src
    
  • .env:

     PYTHONPATH="${PYTHONPATH};./src;"
    
  • pytest.ini (tried with pytest 7.1.2):

     [pytest]
     pythonpath = . src
    
  • ./src/mypkg/appmodule.py:

     def i_hate_configuring_python():
         return "Finally..."
    
  • ./tests/mypkg/appmodule_test.py:

    from mypkg import app_module
    
    
    def test_demo(): 
        print(app_module.i_hate_configuring_python())
    

This should do the trick

Greg7000
  • 297
  • 3
  • 15
-1

Import the package using from .. import mypkg. For this to work you will need to add (empty) __init__.py files to the tests directory and the containing directory. py.test should take care of the rest.

otus
  • 5,572
  • 1
  • 34
  • 48
  • Thanks that might well work but the same guide cautions "avoid `__init__.py` files in your test directories" so I'm wary of doing that http://pytest.org/latest/goodpractises.html#choosing-a-test-layout-import-rules – Colonel Panic Jun 02 '14 at 14:58
  • This wouldn't work in Python 3 as stated by @woody1193 because Python considers test.py as as the top level module therefore using a relative import as you suggested would give you an error ```ValueError: attempted relative import beyond top-level package``` – Apichart Thanomkiet Jan 26 '19 at 15:28
  • @ApichartThanomkiet the question is from 2014 and does not specify python 3. The lack of backwards compatibility broke a lot of import related code and answers. – otus Jan 27 '19 at 07:46