Python:如何运行子目录中所有源文件的unittest.main()?
我正在开发一个带有几个源文件的Python模块,每个源文件都有自己的testing类,源自unittest 。 考虑目录结构:
dirFoo\ test.py dirBar\ __init__.py Foo.py Bar.py
要testingFoo.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”自动searchdirBar任何unit testing派生类,并调用“unittest.main()”。 在实践中做到这一点的最好方法是什么?
我尝试使用Python调用execfile文件中的每个* .py文件在dirBar,运行第一个.py文件find并退出调用test.py,然后我必须通过添加unittest.main()复制我的代码每个源文件都违反了DRY原则。
从Python 2.7开始,testing发现在unittest包中自动完成。 从文档 :
Unittest支持简单的testing发现。 为了与testing发现兼容,所有testing文件都必须是可从项目的顶级目录导入的模块或包(这意味着它们的文件名必须是有效的标识符)。
testing发现在
TestLoader.discover()
,但也可以从命令行使用。 基本的命令行用法是:cd project_directory python -m unittest discover
默认情况下,它会查找名为test*.py
软件包,但是这个可以改变,所以你可以使用类似的东西
python -m unittest discover --pattern=*.py
代替你的test.py脚本。
这是我的testing发现代码,似乎做的工作。 我想确保我可以轻松地扩展testing,而不必在任何相关文件中列出testing,但也要避免将所有testing写入单个“最佳”文件。
所以结构是
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)
我相信这是在一个目录中编写几个testing用例最简单的解决scheme。 该解决scheme需要Python 2.7或Python 3。
我知道有一个明显的解决办法:
dirFoo\ __init__.py test.py dirBar\ Foo.py Bar.py
dirFoo / test.py的内容
from dirBar import * import unittest if __name__ == "__main__": unittest.main()
运行testing:
$ python test.py ........... ---------------------------------------------------------------------- Ran 11 tests in 2.305s OK
对不起,这个愚蠢的问题。
你应该尝试鼻子 。 这是一个图书馆,帮助创buildtesting,它与unit testing或doctest
集成。 所有你需要做的就是运行nosetests
,它会find你所有的单位testing。
% nosetests # finds all tests in all subdirectories % nosetests tests/ # find all tests in the tests directory
我想出了一个可以做你想做的事情的片段。 它沿着你提供的寻找Python包/模块的path,并从这些模块中累积一组testing套件,然后一次执行它们。
关于这一点的好处是,它可以在所有指定目录下嵌套的软件包上工作,而且您不必在添加新组件时手动更改导入。
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)
如果它碰巧帮助任何人,这是我解决这个问题的方法。 我有用例,我有以下的目录结构:
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()
并在每个mypackage/tests/test_category_n/
复制并粘贴一个空的__init__.py
和下面的__main__.py
:
import unittest from .. import prepare_load_tests_function from . import __path__ load_tests = prepare_load_tests_function(__path__) unittest.main()
并在每个实际testing文件中添加标准if __name__ == '__main__': unittest.main()
。
(适用于Windows上的Python 3.3,ymmv。)