Reputation: 42418
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
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
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