如何在目录中运行所有的Pythonunit testing?
我有一个包含我的Pythonunit testing的目录。 每个unit testing模块的forms是test _ *。py 。 我试图做一个名为all_test.py的文件,你会猜到它运行上述testing表单中的所有文件并返回结果。 到目前为止,我尝试了两种方法。 都失败了。 我将展示两种方法,并希望外面的人知道如何正确地做到这一点。
对于我的第一个勇敢的尝试,我想“如果我只是将所有的testing模块导入到文件中,然后调用这个unittest.main()
,它会工作,对不对? 那么,事实certificate我错了。
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()
这不起作用,我得到的结果是:
$ python all_test.py ---------------------------------------------------------------------- Ran 0 tests in 0.000s OK
对于我的第二次尝试,虽然我确定,也许我会尝试以更“手动”的方式来完成整个testing。 所以我试图做到这一点:
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()
这也没有工作,但似乎如此接近!
$ 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
我似乎有一个类似的套件,我可以执行结果。 我有点担心它说我只run=1
,好像应该run=2
,但这是进步。 但是,我如何通过并显示结果为主? 或者我怎么基本上得到它的工作,所以我可以运行这个文件,这样做,运行在这个目录中的所有unit testing?
你可以使用一个testing跑步者为你做这个。 鼻子是非常好的例子。 运行时,它会在当前树中findtesting并运行它们。
更新:
这是我鼻子前几天的一些代码。 你可能不需要明确的模块名称列表,但其余的可能对你有用。
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)
使用Python 2.7及更高版本,您不必编写新代码或使用第三方工具来执行此操作; 通过命令行执行recursiontesting是内置的。
python -m unittest discover <test_directory> # or python -m unittest discover -s <directory> -p '*_test.py'
你可以阅读更多的Python 2.7或Python 3.xunit testing文档。
那么通过研究上面的代码(特别是使用TextTestRunner
和defaultTestLoader
),我能够变得非常接近。 最后我通过将所有testing套件传递给一个套件构造函数来解决我的代码,而不是“手动”添加它们,这就解决了我的其他问题。 所以这是我的解决scheme。
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)
是的,使用鼻子比使用鼻子更容易,但是除此之外。
如果你想从各种testing用例类运行所有的testing,你很乐意明确地指定它们,那么你可以这样做:
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() suite = TestSuite(( loader.loadTestsFromTestCase(TestSymbols), loader.loadTestsFromTestCase(TestPatterns), )) runner = TextTestRunner(verbosity = 2) runner.run(suite)
其中uclid
是我的项目, TestSymbols
和TestPatterns
是TestCase
子类。
这现在可以直接从unittest: unittest.TestLoader.discover 。
loader = unittest.TestLoader() start_dir = 'path/to/your/test/files' suite = loader.discover(start_dir) runner = unittest.TextTestRunner() runner.run(suite)
我已经使用了discover
方法和load_tests
重载来实现这个结果在一个(最小的,我认为)数行代码:
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()
五点执行就像
Ran 27 tests in 0.187s OK
在python 3中,如果你使用的是unittest.TestCase
并且你的testing目录中有一个空的(或者其他的) __init__.py
文件,那么你可以运行所有的testing
python -m unittest
完成! 小于100行的解决scheme。 希望另一个Python初学者通过find这个节省时间。
我尝试了各种方法,但都显得有缺陷,或者我不得不化妆一些代码,这是烦人的。 但是在linux下有一个简单的方法,那就是通过一定的模式find每一个testing,然后逐一调用它们。
find . -name 'Test*py' -exec python '{}' \;
而最重要的是,它绝对有效。
我使用PyDev / LiClipse,并没有真正想出如何从GUI一次运行所有的testing。 (编辑:你右键点击根testing文件夹并selectRun as -> Python unit-test
这是我目前的解决方法:
import unittest def load_tests(loader, tests, pattern): return loader.discover('.') if __name__ == '__main__': unittest.main()
我把这个代码放在一个名为all
的模块中,在我的testing目录中。 如果我从LiClipse运行这个模块作为unittest,那么所有的testing都运行。 如果我要求只重复特定或失败的testing,那么只有这些testing运行。 它不会干扰我的命令行testing运行(nosetests) – 它被忽略。
您可能需要根据您的项目设置更改参数。
如果打包的库或应用程序,你不想这样做。 setuptools
会为你做 。
要使用这个命令,你的项目的testing必须通过一个函数,一个TestCase类或者方法,或者一个包含
TestCase
类的模块或者包来包装在unittest
testing套件中。 如果命名的套件是一个模块,并且该模块具有一个additional_tests()
函数,则会调用它,并将结果(必须是unittest.TestSuite
)添加到要运行的testing中。 如果命名的套件是一个包,则任何子模块和子包将recursion添加到整个testing套件中 。
只要告诉它你的根testing包是什么,就像:
setup( # ... test_suite = 'somepkg.test' )
并运行python setup.py test
。
除非在testing套件中避免相对导入,否则基于文件的发现可能在Python 3中存在问题,因为discover
使用文件导入。 即使它支持可选的top_level_dir
,但我有一些无限的recursion错误。 因此,对于非打包代码的简单解决scheme是将以下内容放在testing包的__init__.py
中(请参阅load_tests协议 )。
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
基于Stephen Cagle的回答,我添加了对嵌套testing模块的支持。
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)
代码search的所有子目录.
*Tests.py
文件,然后加载。 它期望每个*Tests.py
包含一个类*Tests(unittest.TestCase)
,依次加载并执行一个接一个。
这适用于任意深度嵌套的目录/模块,但是其中的每个目录至less需要包含一个空的__init__.py
文件。 这允许testing通过点replace斜杠(或反斜杠)来加载嵌套的模块(请参阅replace_slash_by_dot
)。
因为testing发现似乎是一个完整的主题,所以有一些专门的testing发现框架:
- 鼻子
- Py.Test
更多阅读: https : //wiki.python.org/moin/PythonTestingToolsTaxonomy
这是我的方法,创build一个包装来从命令行运行testing:
#!/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编码标准。
然后,您可以为所有testing创buildBaseTest类,以便用于所有testing的通用组件,因此您的每个testing都将如下所示:
from BaseTest import BaseTest class FooTestPagesBasic(BaseTest): def test_foo(self): driver = self.driver driver.get(self.base_url + "/")
要运行,只需指定testing作为命令行参数的一部分,例如:
./run_tests.py -h http://example.com/ tests/**/*.py