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。)