There are some related questions, but none apply.
This is my directory tree:
» tree abc_backend
abc_backend/
├── backend_main.py
├── FundDatabase.db
├── healthcheck.py
├── __init__.py
├── init.py
├── portfolio.py
├── private.py
├── __pycache__
├── questionnaire.py
├── recurring.py
├── registration.py
├── tests
│ ├── config.py
│ ├── __init__.py
│ ├── __pycache__
│ ├── test_backend.py
│ ├── test_healthcheck.py
│ └── test_private.py
├── trading.py
├── Users.db
├── VERSION
└── visualisation.py
unittest
is not able to find anything:
top » python -m unittest abc_backend
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
Not even from within abc_backend
:
abc_backend » python -m unittest tests
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
What I have already verified:
test_whatever
)unittest.TestCase
abc_backend
and the abc_backend/tests
directories have an (empty) __init__.py
unittest discover
finds the tests, but has problems with relative imports (see below)nose
is able to discover and run the tests, no problemsI would like to understand:
discover
to unittest
to force it to discover the tests? What does unittest
do without the discover
sub-command? (I thought unittest does test discovery by default). According to the documentation: python -m unittest is the equivalent of python -m unittest discover
discover
sub-command), why do I have import issues?» python
Python 3.4.3 (default, Oct 14 2015, 20:28:29)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import abc_backend.tests
>>> import abc_backend.tests.test_private
>>> import abc_backend.tests.test_healthcheck
>>> import abc_backend.tests.test_backend
If I run it from the top dir:
top » python -m unittest discover abc_backend
======================================================================
ERROR: tests.test_private (unittest.loader.ModuleImportFailure)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python3.4/unittest/case.py", line 58, in testPartExecutor
yield
File "/usr/lib/python3.4/unittest/case.py", line 577, in run
testMethod()
File "/usr/lib/python3.4/unittest/loader.py", line 32, in testFailure
raise exception
ImportError: Failed to import test module: tests.test_private
Traceback (most recent call last):
File "/usr/lib/python3.4/unittest/loader.py", line 312, in _find_tests
module = self._get_module_from_name(name)
File "/usr/lib/python3.4/unittest/loader.py", line 290, in _get_module_from_name
__import__(name)
File "/foo/bar/abc_backend/tests/test_private.py", line 6, in <module>
from .. import init
ValueError: attempted relative import beyond top-level package
If I run it from within abc_backend
:
abc_backend » python -m unittest discover tests
======================================================================
ERROR: test_private (unittest.loader.ModuleImportFailure)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python3.4/unittest/case.py", line 58, in testPartExecutor
yield
File "/usr/lib/python3.4/unittest/case.py", line 577, in run
testMethod()
File "/usr/lib/python3.4/unittest/loader.py", line 32, in testFailure
raise exception
ImportError: Failed to import test module: test_private
Traceback (most recent call last):
File "/usr/lib/python3.4/unittest/loader.py", line 312, in _find_tests
module = self._get_module_from_name(name)
File "/usr/lib/python3.4/unittest/loader.py", line 290, in _get_module_from_name
__import__(name)
File "/foo/bar/abc_backend/tests/test_private.py", line 6, in <module>
from .. import init
SystemError: Parent module '' not loaded, cannot perform relative import
I reproduced all the problems with CPython 3.5, so my answer should be relevant to both 3.4 and 3.5.
The reason why there are issues with relative imports is that due to
specifics of invocations you really do not import abc_backend
package.
First, let’s take a look at
top» python3 -m unittest discover abc_backend
When you run tests from top that way, abc_backend
is just not imported.
That is because /home/user/top/abc_backend
is added to sys.path instead
of /home/user/top
. To solve this problem, do
top» python3 -m unittest discover abc_backend -t .
Now, about the in-abc_backend invocation. When you do
abc_backend» python3 -m unittest discover tests
abc_backend
is not importable, as /home/user/top/abc_backend/tests
dir does not contain abc_backend
package. This too can be solved with
abc_backend» python3 -m unittest discover tests -t ../
that will correctly put /home/user/top
dir (pun intended) into sys.path
.
The -t
(or --top-level-directory
) option sets top level directory
of project and defaults to start directory (which is .
by default).
So, what is in sys.path
is important, as that affects imports, which
affect test loading, as discovery loads tests using import machinery.
-m unittest
and -m unittest discover
When you do
top» python3 -m unittest abc_backend
in reality you are running unittest/__main__.py
file. There
main(module=None)
is invoked, and eventually you get to
loadTestsFromModule
that does
tests = []
for name in dir(module):
obj = getattr(module, name)
if isinstance(obj, type) and issubclass(obj, case.TestCase):
tests.append(self.loadTestsFromTestCase(obj))
As abc_backend/__init__.py
does not contain any test cases,
isinstance(obj, type) and issubclass(obj, case.TestCase)
returns
False
for all module members (so tests
is empty).
To make this particular way of invocation work, you’ll have to do
what people usually did in pre-discover
times (aside from non-stdlib
frameworks): manually import cases from test modules (or
maybe construct test suite according to load_tests
protocol).
So, how
top» python3 -m unittest discover abc_backend
differs?
Basically, differences may be expressed as following conditional:
if len(argv) > 1 and argv[1].lower() == 'discover':
# -m unittest discover
loader.discover(...)
else:
# -m unittest
loader.loadTestsFromNames(...)
When argv
is ['python3 -m unittest', 'discover', 'abc_backend']
,
actual discovery mechanism is used. When argv
is ['python3 -m unittest', 'abc_backend']
,
loadTestsFromNames
is used, which calls loadTestsFromModule
at some point, and no tests are found.
That’s the way things are in unittest/main.py
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With