递归单元测试发现

Ada*_*ith 29 python python-2.7 python-unittest

我有一个包含目录"tests"的包,我正在存储我的单元测试.我的包看起来像:

.
??? LICENSE
??? models
?   ??? __init__.py
??? README.md
??? requirements.txt
??? tc.py
??? tests
?   ??? db
?   ?   ??? test_employee.py
?   ??? test_tc.py
??? todo.txt
Run Code Online (Sandbox Code Playgroud)

从我的包目录,我希望能够找到tests/test_tc.pytests/db/test_employee.py.我不想安装第三方库(nose或其他),或者必须手动构建一个TestSuite以运行它.

当然有一种方法可以告诉unittest discover他们一旦发现测试就不要停止寻找?python -m unittest discover -s tests会发现tests/test_tc.pypython -m unittest discover -s tests/db会找到tests/db/test_employee.py.有没有找到两者的方法?

Ada*_*ith 62

在进行一些挖掘时,似乎只要更深层次的模块仍可导入,它们就会被发现python -m unittest discover.然后,解决方案只是将__init__.py文件添加到每个目录以使其成为包.

.
??? LICENSE
??? models
?   ??? __init__.py
??? README.md
??? requirements.txt
??? tc.py
??? tests
?   ??? db
?   ?   ??? __init__.py       # NEW
?   ?   ??? test_employee.py
?   ??? __init__.py           # NEW
?   ??? test_tc.py
??? todo.txt
Run Code Online (Sandbox Code Playgroud)

只要每个目录都有一个__init__.py,python -m unittest discover就可以导入相关test_*模块.

  • 我认为这是正确的答案. (7认同)

jed*_*rds 7

如果您可以__init__.py在测试中添加文件,则可以在其中放置一个可以load_tests为您处理发现的函数.

如果测试包名称(带有目录__init__.py)与模式匹配,则将检查包的"load_tests"函数.如果存在,那么将使用loader,tests,pattern调用它.

如果load_tests存在,那么发现操作 递归到包装,load_tests负责加载所有测试包.

我很不相信这是最好的方法,但编写该功能的一种方法是:

import os
import pkgutil
import inspect
import unittest

# Add *all* subdirectories to this module's path
__path__ = [x[0] for x in os.walk(os.path.dirname(__file__))]

def load_tests(loader, suite, pattern):
    for imp, modname, _ in pkgutil.walk_packages(__path__):
        mod = imp.find_module(modname).load_module(modname)
        for memname, memobj in inspect.getmembers(mod):
            if inspect.isclass(memobj):
                if issubclass(memobj, unittest.TestCase):
                    print("Found TestCase: {}".format(memobj))
                    for test in loader.loadTestsFromTestCase(memobj):
                        print("  Found Test: {}".format(test))
                        suite.addTest(test)

    print("=" * 70)
    return suite
Run Code Online (Sandbox Code Playgroud)

非常难看,我同意.

首先,将所有子目录添加到测试包的路径(Docs).

然后,您使用pkgutil走路径,寻找包或模块.

当它找到一个时,它会检查模块成员以查看它们是否是类,如果它们是类,它们是否是它们的子类unittest.TestCase.如果是,则将类内的测试加载到测试套件中.

现在,从项目根目录中,您可以输入

python -m unittest discover -p tests
Run Code Online (Sandbox Code Playgroud)

使用-p模式开关.如果一切顺利,你会看到我所看到的,这是:

Found TestCase: <class 'test_tc.TestCase'>
  Found Test: testBar (test_tc.TestCase)
  Found Test: testFoo (test_tc.TestCase)
Found TestCase: <class 'test_employee.TestCase'>
  Found Test: testBar (test_employee.TestCase)
  Found Test: testFoo (test_employee.TestCase)
======================================================================
....
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK
Run Code Online (Sandbox Code Playgroud)

这是什么预期,我的每一个两个示例文件包含了两个测试,testFootestBar每一个.

编辑:经过一些挖掘后,看起来您可以将此功能指定为:

def load_tests(loader, suite, pattern):
    for imp, modname, _ in pkgutil.walk_packages(__path__):
        mod = imp.find_module(modname).load_module(modname)
        for test in loader.loadTestsFromModule(mod):
            print("Found Tests: {}".format(test._tests))
            suite.addTests(test)
Run Code Online (Sandbox Code Playgroud)

这使用loader.loadTestsFromModule()方法而不是loader.loadTestsFromTestCase()我上面使用的方法.它仍然修改了tests包路径并走向它寻找模块,我认为这是关键.

现在输出看起来有点不同,因为我们一次在主要测试套件中添加一个找到的测试套件suite:

python -m unittest discover -p tests
Found Tests: [<test_tc.TestCase testMethod=testBar>, <test_tc.TestCase testMethod=testFoo>]
Found Tests: [<test_employee.TestCase testMethod=testBar>, <test_employee.TestCase testMethod=testFoo>]
======================================================================
....
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK
Run Code Online (Sandbox Code Playgroud)

但是我们仍然可以在两个子目录中获得我们预期的4个测试.