我正在开发一个包含多个源文件的Python模块,每个源文件都有源自unittest的自己的测试类。考虑目录结构:
dirFoo\
test.py
dirBar\
__init__.py
Foo.py
Bar.py
要测试Foo.py或Bar.py,我会在Foo.py和Bar.py源文件的末尾添加它:
if __name__ == "__main__":
unittest.main()
在任何一个源上运行Python,即
$ python Foo.py
...........
----------------------------------------------------------------------
Ran 11 tests in 2.314s
OK
理想情况下,我会“test.py”自动搜索dirBar以获取任何unittest派生类,并调用“unittest.main()”。在实践中这样做的最佳方法是什么?
我尝试使用Python为dirBar中的每个* .py文件调用execfile,该文件针对找到的第一个.py文件运行一次&退出调用test.py,然后我必须通过在每个源文件中添加unittest.main()来复制我的代码 - 这违反了DRY原则。
答案 0 :(得分:59)
从Python 2.7开始,测试发现在unittest包中自动完成。来自docs:
Unittest支持简单的测试发现。为了兼容 通过测试发现,所有测试文件必须是模块或包 可以从项目的顶级目录导入(这意味着 他们的文件名必须是有效的标识符。)
测试发现在
TestLoader.discover()
中实现,但也可以 从命令行使用。基本命令行用法是:cd project_directory python -m unittest discover
默认情况下,它会查找名为test*.py
的软件包,但这可以更改,因此您可以使用类似
python -m unittest discover --pattern=*.py
代替test.py脚本。
答案 1 :(得分:28)
这是我的测试发现代码,似乎可以完成这项工作。我想确保我可以轻松地扩展测试,而不必在任何涉及的文件中列出它们,但也避免在一个单独的Übertest文件中编写所有测试。
所以结构是
myTests.py
testDir\
__init__.py
testA.py
testB.py
myTest.py看起来像这样:
import unittest
if __name__ == '__main__':
testsuite = unittest.TestLoader().discover('.')
unittest.TextTestRunner(verbosity=1).run(testsuite)
我相信这是在一个目录中编写多个测试用例的最简单的解决方案。该解决方案需要Python 2.7或Python 3。
答案 2 :(得分:26)
我知道有一个明显的解决方案:
dirFoo\
__init__.py
test.py
dirBar\
__init__.py
Foo.py
Bar.py
dirFoo / test.py的内容
from dirBar import *
import unittest
if __name__ == "__main__":
unittest.main()
运行测试:
$ python test.py
...........
----------------------------------------------------------------------
Ran 11 tests in 2.305s
OK
抱歉这个愚蠢的问题。
答案 3 :(得分:19)
您应该尝试nose。它是一个帮助创建测试的库,它与unittest
或doctest
集成在一起。您需要做的只是运行nosetests
,它会为您找到所有的单元测试。
% nosetests # finds all tests in all subdirectories
% nosetests tests/ # find all tests in the tests directory
答案 4 :(得分:2)
我想出了一个可能做你想做的片段。它走的是你提供的路径,寻找Python包/模块,并从这些模块中累积一组测试套件,然后一次性执行。
关于这一点的好处是,它将适用于嵌套在您指定的目录下的所有包,并且您不必在添加新组件时手动更改导入。
import logging
import os
import unittest
MODULE_EXTENSIONS = set('.py .pyc .pyo'.split())
def unit_test_extractor(tup, path, filenames):
"""Pull ``unittest.TestSuite``s from modules in path
if the path represents a valid Python package. Accumulate
results in `tup[1]`.
"""
package_path, suites = tup
logging.debug('Path: %s', path)
logging.debug('Filenames: %s', filenames)
relpath = os.path.relpath(path, package_path)
relpath_pieces = relpath.split(os.sep)
if relpath_pieces[0] == '.': # Base directory.
relpath_pieces.pop(0) # Otherwise, screws up module name.
elif not any(os.path.exists(os.path.join(path, '__init__' + ext))
for ext in MODULE_EXTENSIONS):
return # Not a package directory and not the base directory, reject.
logging.info('Base: %s', '.'.join(relpath_pieces))
for filename in filenames:
base, ext = os.path.splitext(filename)
if ext not in MODULE_EXTENSIONS: # Not a Python module.
continue
logging.info('Module: %s', base)
module_name = '.'.join(relpath_pieces + [base])
logging.info('Importing from %s', module_name)
module = __import__(module_name)
module_suites = unittest.defaultTestLoader.loadTestsFromModule(module)
logging.info('Got suites: %s', module_suites)
suites += module_suites
def get_test_suites(path):
""":return: Iterable of suites for the packages/modules
present under :param:`path`.
"""
logging.info('Base path: %s', package_path)
suites = []
os.path.walk(package_path, unit_test_extractor, (package_path, suites))
logging.info('Got suites: %s', suites)
return suites
if __name__ == '__main__':
logging.basicConfig(level=logging.WARN)
package_path = os.path.dirname(os.path.abspath(__file__))
suites = get_test_suites(package_path)
for suite in suites:
unittest.TextTestRunner(verbosity=2).run(suite)
答案 5 :(得分:1)
如果它碰巧帮助了任何人,这就是我解决这个问题的方法。我有一个用例,我有以下目录结构:
mypackage/
tests/
test_category_1/
tests_1a.py
tests_1b.py
...
test_category_2/
tests_2a.py
tests_2b.py
...
...
我希望以下所有内容以明显的方式工作,并且能够提供与unittest接受的相同的命令行参数:
python -m mypackage.tests
python -m mypackage.tests.test_category_1
python -m mypackage.tests.test_category_1.tests_1a
解决方案是设置mypackage/tests/__init__.py
,如下所示:
import unittest
def prepare_load_tests_function (the__path__):
test_suite = unittest.TestLoader().discover(the__path__[0])
def load_tests (_a, _b, _c):
return test_suite
return load_tests
并像这样设置mypackage/tests/__main__.py
:
import unittest
from . import prepare_load_tests_function, __path__
load_tests = prepare_load_tests_function(__path__)
unittest.main()
并在每个__init__.py
中复制并粘贴空的__main__.py
和以下mypackage/tests/test_category_n/
:
import unittest
from .. import prepare_load_tests_function
from . import __path__
load_tests = prepare_load_tests_function(__path__)
unittest.main()
并在每个实际测试文件中添加标准if __name__ == '__main__': unittest.main()
。
(适用于Windows上的Python 3.3,ymmv。)