递归unittest dis

2024-05-21 14:54:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个包含目录“tests”的包,其中存储了我的单元测试。我的包裹看起来像:

.
├── LICENSE
├── models
│   └── __init__.py
├── README.md
├── requirements.txt
├── tc.py
├── tests
│   ├── db
│   │   └── test_employee.py
│   └── test_tc.py
└── todo.txt

从我的包目录中,我希望能够同时找到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。不是有办法找到两者吗?


Tags: pytest目录txtdbinitlicensemodels
2条回答

如果您可以在测试中添加一个__init__.py文件,那么您可以在那里放置一个load_tests函数来处理发现。

If a test package name (directory with __init__.py) matches the pattern then the package will be checked for a 'load_tests' function. If this exists then it will be called with loader, tests, pattern.

If load_tests exists then discovery does not recurse into the package, load_tests is responsible for loading all tests in the package.

我并不相信这是最好的方法,但编写该函数的一种方法是:

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

很难看,我同意。

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

然后,使用^{}遍历路径,查找包或模块。

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

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

python -m unittest discover -p tests

使用-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

这正是预期的,我的两个示例文件中的每个都包含两个测试,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)

这使用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

但是我们仍然可以在两个类和两个子目录中得到我们期望的4个测试。

在做一些深入研究时,似乎只要深层模块仍然是可导入的,它们就会通过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

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

相关问题 更多 >