camh
camh

Reputation: 42418

How do I run unit tests in package directory

I have a python code base where I have refactored a module (file) into a package (directory) as the file was getting a bit large and unmanageable. However, I cannot get my unit tests running as desired with the new structure.

I place my unit test files directly alongside the code it tests (this is a requirement and cannot change - no separate test directories):

app/
+-- app.py
+-- config.py
+-- config_test.py
+-- model/
|   +-- __init__.py
|   +-- base.py
|   +-- base_test.py
|   +-- square.py
|   +-- square_test.py
+-- test.py
+-- web.py
+-- web_test.py

Previously, the model package was the model.py module with a model_test.py test suite.

There is a top-level test runner - test.py and that works fine. It finds the test cases inside the model directory and runs them successfully (it uses the discovery feature of unittest - see end of post for test.py):

$ python test.py

However, I also want to be able to directly run the test cases in the model directory:

$ python model/base_test.py

This does not work, because the test is inside the package directory. The imports in the code fail because they are either not in a module when imported directly by the test suite or the search path is wrong.

For instance, in model/square.py, I can import base.py in one of two ways:

from model import Base

or

from .base import Base

These both work fine when model is imported. But when inside the model test suites, I cannot import square because square cannot import base.

square_test.py contains imports like:

import unittest
from square import Square
... test cases ...
if __name__ == '__main__':
    unittest.main()

For the first type of import in square.py (from model import Base), I get the error:

ModuleNotFoundError: No module named 'model'

Fair enough, sys.path has /home/camh/app/model and there is no model module in there.

For the second type of import in square.py (from .base import Base), I get the error:

ImportError: attempted relative import with no known parent package

I cannot figure out how to do my imports that allows me to have tests alongside the unit-under-test and be directly runnable. I want directly runnable test suites as often I do not want to run the entire set of tests, but just target individual tests:

$ python model/square_test.py SquareTest.test_equal_sides

I cannot do that with my test runner because it just uses discovery to run all the tests and discovery is not compatible with specifying individual test suites or test functions.

My test runner (test.py) is just:

import os, sys
sys.argv += ['discover', os.path.dirname(sys.argv[0]), '*_test.py']
unittest.main(module=None)

Upvotes: 1

Views: 1707

Answers (2)

Gang
Gang

Reputation: 2768

suggestions:

add app/__init__.py, and treat app as package instead of model

one way is for all tests, using explicit from app.model.square import Square

The relative import should be fine, as long as using nosetests -vw . in app/ directory.

These all under the price of removing app/test.py

Another common mistake after re-factoring, is the .pyc not all removed and re-generated.

Upvotes: 0

Jeremy
Jeremy

Reputation: 1358

You can invoke the unittest module from the command line with arguments:

python -m unittest model.square_test

If you are using python3 you can use file names too:

python3 -m unittest model/square_test.py

Upvotes: 6

Related Questions