How do I run all Python unit tests in a directory?
我有一个包含Python单元测试的目录。每个单元测试模块都是形式测试。我正试图创建一个名为all_test.py的文件,您猜对了,它将运行上述测试表单中的所有文件并返回结果。到目前为止,我试过两种方法,都失败了。我将展示这两种方法,我希望有人知道如何正确地做到这一点。
对于我的第一次勇敢尝试,我想,"如果我只是导入文件中的所有测试模块,然后调用这个
1 2 3 4 5 6 7 8 9 | import glob import unittest testSuite = unittest.TestSuite() test_file_strings = glob.glob('test_*.py') module_strings = [str[0:len(str)-3] for str in test_file_strings] if __name__ =="__main__": unittest.main() |
这不起作用,我得到的结果是:
1 2 3 4 5 6 | $ python all_test.py ---------------------------------------------------------------------- Ran 0 tests in 0.000s OK |
对于我的第二次尝试,我想,好吧,也许我会尝试用一种更"手动"的方式来做整个测试。所以我尝试在下面这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import glob import unittest testSuite = unittest.TestSuite() test_file_strings = glob.glob('test_*.py') module_strings = [str[0:len(str)-3] for str in test_file_strings] [__import__(str) for str in module_strings] suites = [unittest.TestLoader().loadTestsFromName(str) for str in module_strings] [testSuite.addTest(suite) for suite in suites] print testSuite result = unittest.TestResult() testSuite.run(result) print result #Ok, at this point I have a result #How do I display it as the normal unit test command line output? if __name__ =="__main__": unittest.main() |
这也不起作用,但似乎如此接近!
1 2 3 4 5 6 7 8 | $ python all_test.py <unittest.TestSuite tests=[<unittest.TestSuite tests=[<unittest.TestSuite tests=[<test_main.TestMain testMethod=test_respondes_to_get>]>]>]> <unittest.TestResult run=1 errors=0 failures=0> ---------------------------------------------------------------------- Ran 0 tests in 0.000s OK |
我似乎有某种类型的套件,我可以执行结果。我有点担心它说我只有
对于Python2.7及更高版本,您不必编写新代码或使用第三方工具来完成这项工作;通过命令行进行递归测试是内置的。
1 2 3 | python -m unittest discover <test_directory> # or python -m unittest discover -s <directory> -p '*_test.py' |
您可以在python 2.7中阅读更多内容或python 3.x unittest文档。
您可以使用一个测试运行程序来完成这项工作。例如,鼻子很好。运行时,它将在当前树中找到测试并运行它们。
更新:
这是我鼻子前几天的一些代码。您可能不需要模块名称的显式列表,但其余的可能对您有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | testmodules = [ 'cogapp.test_makefiles', 'cogapp.test_whiteutils', 'cogapp.test_cogapp', ] suite = unittest.TestSuite() for t in testmodules: try: # If the module defines a suite() function, call it to get the suite. mod = __import__(t, globals(), locals(), ['suite']) suitefn = getattr(mod, 'suite') suite.addTest(suitefn()) except (ImportError, AttributeError): # else, just load all the test cases from the module. suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t)) unittest.TextTestRunner().run(suite) |
型
现在可以直接从unittest:unittest.testloader.discover进行此操作。
1 2 3 4 5 6 7 | import unittest loader = unittest.TestLoader() start_dir = 'path/to/your/test/files' suite = loader.discover(start_dir) runner = unittest.TextTestRunner() runner.run(suite) |
型
在python 3中,如果您使用的是
- 百万千克1您的
型
然后,可以使用以下命令运行所有测试:
1 | python -m unittest |
。
完成!少于100行的解决方案。希望另一个Python初学者通过找到这个来节省时间。
通过研究上面的代码(特别是使用
1 2 3 4 5 6 7 8 | import glob import unittest test_files = glob.glob('test_*.py') module_strings = [test_file[0:len(test_file)-3] for test_file in test_files] suites = [unittest.defaultTestLoader.loadTestsFromName(test_file) for test_file in module_strings] test_suite = unittest.TestSuite(suites) test_runner = unittest.TextTestRunner().run(test_suite) |
是的,仅仅用鼻子可能比用鼻子更容易,但这也不重要。
如果您想要运行来自不同测试用例类的所有测试,并且您很乐意显式地指定它们,那么您可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from unittest import TestLoader, TextTestRunner, TestSuite from uclid.test.test_symbols import TestSymbols from uclid.test.test_patterns import TestPatterns if __name__ =="__main__": loader = TestLoader() tests = [ loader.loadTestsFromTestCase(test) for test in (TestSymbols, TestPatterns) ] suite = TestSuite(tests) runner = TextTestRunner(verbosity=2) runner.run(suite) |
其中
我已经使用了
1 2 3 4 5 6 7 8 9 10 11 | def load_tests(loader, tests, pattern): ''' Discover and load all unit tests in all files named ``*_test.py`` in ``./src/`` ''' suite = TestSuite() for all_test_suite in unittest.defaultTestLoader.discover('src', pattern='*_tests.py'): for test_suite in all_test_suite: suite.addTests(test_suite) return suite if __name__ == '__main__': unittest.main() |
在五个人身上执行类似
1 2 | Ran 27 tests in 0.187s OK |
我尝试了各种各样的方法,但似乎都有缺陷,或者我必须编写一些代码,这很烦人。但是在Linux下有一种方便的方法,那就是通过特定的模式找到每个测试,然后逐个调用它们。
1 | find . -name 'Test*py' -exec python '{}' \; |
最重要的是,它确实有效。
型
对于打包的库或应用程序,您不想这样做。我会帮你的。
To use this command, your project’s tests must be wrapped in a
unittest test suite by either a function, a TestCase class or method, or a module or package containingTestCase classes. If the named suite is a module, and the module has anadditional_tests() function, it is called and the result (which must be aunittest.TestSuite ) is added to the tests to be run. If the named suite is a package, any submodules and subpackages are recursively added to the overall test suite.
号
只需告诉它您的根测试包在哪里,比如:
1 2 3 4 | setup( # ... test_suite = 'somepkg.test' ) |
运行
在python 3中,基于文件的发现可能有问题,除非避免在测试套件中进行相对导入,因为
1 2 3 4 5 6 7 8 9 10 11 | import unittest from . import foo, bar def load_tests(loader, tests, pattern): suite = unittest.TestSuite() suite.addTests(loader.loadTestsFromModule(foo)) suite.addTests(loader.loadTestsFromModule(bar)) return suite |
号
我使用pydev/liclipse,还没有真正了解如何从GUI一次运行所有测试。(编辑:右击根测试文件夹,选择
这是我目前的解决方法:
1 2 3 4 5 6 7 | import unittest def load_tests(loader, tests, pattern): return loader.discover('.') if __name__ == '__main__': unittest.main() |
我把这个代码放在我的测试目录中一个名为
您可能需要根据项目设置将参数更改为
型
这个bash脚本将从文件系统中的任何地方执行python unittest目录,不管您在哪个工作目录中:它的工作目录总是位于该
所有测试,独立$pwd
UnitTest python模块对当前目录很敏感,除非您告诉它在哪里(使用
当您停留在
1 2 3 4 5 6 | #!/bin/bash this_program="$0" dirname="`dirname $this_program`" readlink="`readlink -e $dirname`" python -m unittest discover -s"$readlink"/test -v |
选定测试,独立$pwd
我将此实用程序文件命名为:
1 | runone.py <test-python-filename-minus-dot-py-fileextension> |
。
1 2 3 4 5 6 | #!/bin/bash this_program="$0" dirname="`dirname $this_program`" readlink="`readlink -e $dirname`" (cd"$dirname"/test; python -m unittest $1) |
。
不需要使用
基于StephenCagle的答案,我增加了对嵌套测试模块的支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import fnmatch import os import unittest def all_test_modules(root_dir, pattern): test_file_names = all_files_in(root_dir, pattern) return [path_to_module(str) for str in test_file_names] def all_files_in(root_dir, pattern): matches = [] for root, dirnames, filenames in os.walk(root_dir): for filename in fnmatch.filter(filenames, pattern): matches.append(os.path.join(root, filename)) return matches def path_to_module(py_file): return strip_leading_dots( \ replace_slash_by_dot( \ strip_extension(py_file))) def strip_extension(py_file): return py_file[0:len(py_file) - len('.py')] def replace_slash_by_dot(str): return str.replace('\', '.').replace('/', '.') def strip_leading_dots(str): while str.startswith('.'): str = str[1:len(str)] return str module_names = all_test_modules('.', '*Tests.py') suites = [unittest.defaultTestLoader.loadTestsFromName(mname) for mname in module_names] testSuite = unittest.TestSuite(suites) runner = unittest.TextTestRunner(verbosity=1) runner.run(testSuite) |
代码搜索
这适用于任意深度嵌套目录/模块,但其中的每个目录至少需要包含一个空的
因为测试发现似乎是一个完整的主题,所以有一些专用的测试发现框架:
- 鼻子
- P.试验
更多信息请阅读:https://wiki.python.org/moin/pythontestingtoolstaxonomy
下面是我的方法,通过创建一个包装器来从命令行运行测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #!/usr/bin/env python3 import os, sys, unittest, argparse, inspect, logging if __name__ == '__main__': # Parse arguments. parser = argparse.ArgumentParser(add_help=False) parser.add_argument("-?","--help", action="help", help="show this help message and exit" ) parser.add_argument("-v","--verbose", action="store_true", dest="verbose", help="increase output verbosity" ) parser.add_argument("-d","--debug", action="store_true", dest="debug", help="show debug messages" ) parser.add_argument("-h","--host", action="store", dest="host", help="Destination host" ) parser.add_argument("-b","--browser", action="store", dest="browser", help="Browser driver.", choices=["Firefox","Chrome","IE","Opera","PhantomJS"] ) parser.add_argument("-r","--reports-dir", action="store", dest="dir", help="Directory to save screenshots.", default="reports") parser.add_argument('files', nargs='*') args = parser.parse_args() # Load files from the arguments. for filename in args.files: exec(open(filename).read()) # See: http://codereview.stackexchange.com/q/88655/15346 def make_suite(tc_class): testloader = unittest.TestLoader() testnames = testloader.getTestCaseNames(tc_class) suite = unittest.TestSuite() for name in testnames: suite.addTest(tc_class(name, cargs=args)) return suite # Add all tests. alltests = unittest.TestSuite() for name, obj in inspect.getmembers(sys.modules[__name__]): if inspect.isclass(obj) and name.startswith("FooTest"): alltests.addTest(make_suite(obj)) # Set-up logger verbose = bool(os.environ.get('VERBOSE', args.verbose)) debug = bool(os.environ.get('DEBUG', args.debug)) if verbose or debug: logging.basicConfig( stream=sys.stdout ) root = logging.getLogger() root.setLevel(logging.INFO if verbose else logging.DEBUG) ch = logging.StreamHandler(sys.stdout) ch.setLevel(logging.INFO if verbose else logging.DEBUG) ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(name)s: %(message)s')) root.addHandler(ch) else: logging.basicConfig(stream=sys.stderr) # Run tests. result = unittest.TextTestRunner(verbosity=2).run(alltests) sys.exit(not result.wasSuccessful()) |
为了简单起见,请原谅我的非PEP8编码标准。
然后,您可以为所有测试的公共组件创建basetest类,因此您的每个测试看起来都很简单:
1 2 3 4 5 | from BaseTest import BaseTest class FooTestPagesBasic(BaseTest): def test_foo(self): driver = self.driver driver.get(self.base_url +"/") |
要运行,只需将测试指定为命令行参数的一部分,例如:
1 | ./run_tests.py -h http://example.com/ tests/**/*.py |