unittest无法发现/运行测试

dan*_*ast 7 python unit-testing nose python-import python-unittest

一些 相关问题,但都不适用.

这是我的目录树:

» 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
Run Code Online (Sandbox Code Playgroud)

unittest 找不到任何东西:

top » python -m unittest abc_backend

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK
Run Code Online (Sandbox Code Playgroud)

甚至不在内部abc_backend:

abc_backend » python -m unittest tests

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK
Run Code Online (Sandbox Code Playgroud)

我已经验证的内容:

  • 我的测试方法正确命名(test_whatever)
  • 我的测试用例扩展了 unittest.TestCase
  • abc_backendabc_backend/tests目录有一个(空)__init__.py
  • 所有测试模块都是可导入的(见下文)
  • unittest discover 找到测试,但相对导入有问题(见下文)
  • nose 能够发现并运行测试,没有问题

我想了解:

  • 为什么我需要传递discoverunittest迫使它发现测试?这是什么unittest做的,而不discover子命令?(我认为unittest默认测试发现).根据文件:

python -m unittest相当于python -m unittest discover

  • 一旦发现测试(通过强制discover子命令),为什么我有导入问题?

测试模块是可导入的

» 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
Run Code Online (Sandbox Code Playgroud)

unittest discover有相对进口的问题

如果我从顶部目录运行它:

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
Run Code Online (Sandbox Code Playgroud)

如果我从内部运行它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
Run Code Online (Sandbox Code Playgroud)

小智 8

我重现了CPython 3.5的所有问题,所以我的答案应该与3.4和3.5相关.

相对进口问题

相对导入存在问题的原因是由于调用的细节,您实际上不会导入abc_backend包.

首先,我们来看看

top» python3 -m unittest discover abc_backend
Run Code Online (Sandbox Code Playgroud)

当你从那个顶部运行测试时,abc_backend就是没有导入.那是因为/home/user/top/abc_backend添加到sys.path而不是/home/user/top.要解决这个问题,请

top» python3 -m unittest discover abc_backend -t .
Run Code Online (Sandbox Code Playgroud)

现在,关于in-abc_backend调用.当你这样做

abc_backend» python3 -m unittest discover tests
Run Code Online (Sandbox Code Playgroud)

abc_backend不可导入,因为/home/user/top/abc_backend/tests dir不包含abc_backend包.这也可以解决

abc_backend» python3 -m unittest discover tests -t ../
Run Code Online (Sandbox Code Playgroud)

这将正确地将/home/user/topdir(双关语)放入sys.path.

-t(或--top-level-directory)选项设置的项目和默认的顶级目录启动目录(这是.默认情况下).因此,sys.path重要的是,因为发现会使用导入机制加载测试,因此会影响测试负载的导入.

-m unittest和之间的区别-m unittest discover

当你这样做

top» python3 -m unittest abc_backend
Run Code Online (Sandbox Code Playgroud)

实际上你正在运行unittest/__main__.py文件.有 main(module=None)调用,最终你 loadTestsFromModule会这样做

tests = []
for name in dir(module):
    obj = getattr(module, name)
    if isinstance(obj, type) and issubclass(obj, case.TestCase):
        tests.append(self.loadTestsFromTestCase(obj))
Run Code Online (Sandbox Code Playgroud)

由于abc_backend/__init__.py不包含任何测试用例,因此 isinstance(obj, type) and issubclass(obj, case.TestCase)返回 False所有模块成员(因此tests为空).

为了使这种特殊的调用方式发挥作用,你必须做人们通常在之前做过的事情discover(除了非stdlib框架):从测试模块手动导入案例(或者根据load_tests协议构建测试套件).

又怎样

top» python3 -m unittest discover abc_backend
Run Code Online (Sandbox Code Playgroud)

不同?

基本上,差异可以表示为以下条件:

if len(argv) > 1 and argv[1].lower() == 'discover':
    # -m unittest discover
    loader.discover(...)
else:
    # -m unittest
    loader.loadTestsFromNames(...)
Run Code Online (Sandbox Code Playgroud)

argv['python3 -m unittest', 'discover', 'abc_backend']时,实际使用的发现机制.如果argv['python3 -m unittest', 'abc_backend'], loadTestsFromNames使用,这就要求loadTestsFromModule在某些时候,并没有测试被发现.这就是事情的方式unittest/main.py.