我怎么运行的所有Python单元的测试,在一个目录?
-
20-09-2019 - |
题
我有一个目录,包含我的蟒蛇单元的测试。每个单元的测试模块的形式 test_*.py.我在尝试做一个文件叫 all_test.py 那会,你猜对了,运行的所有文件在上述试验形式和返回的结果。我们试两种方法为止;两个都失败了。我将显示的两种方法,我希望有人知道如何实际这样做是正确的。
我的第一个勇敢的尝试,我想"如果我只是进我所有的测试模块的文件,然后叫这个 unittest.main()
装饰物,它将工作,对吗?" 嗯,事实证明我是错误的。
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
我的第二次尝试,我,虽然,好吧,也许我将试图做这个测试的事情更多的"手册"时尚。所以我试图这样做如下:
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
, 但这是取得进展。但我怎么通过和显示的结果主要?或者我怎么基本上获得它的工作这样我就可以运行这个文件,并在这样做时,运行的所有单元的测试这个目录?
解决方案
您可以使用一个测试运行器会为你做这个。 鼻子是例如非常好。在运行时,它会发现在当前树测试和运行它们。
更新:
下面是从我的前鼻天一些代码。你可能不希望模块名称的显式列表,但也许剩下的将是对你有用。
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.TestLoader。发现。
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 中,如果您使用 unittest.TestCase
:
- 您必须有一个空的(或其他)
__init__.py
文件在你的test
目录 (必须 被命名test/
) - 你的测试文件在里面
test/
匹配模式test_*.py
. 。它们可以位于以下子目录中test/
, ,并且这些子目录可以命名为任何名称。
然后,您可以使用以下命令运行所有测试:
python -m unittest
完毕!少于 100 行的解决方案。希望另一个Python初学者能找到这个来节省时间。
通过井(特别是使用TextTestRunner
和defaultTestLoader
)研究比特以上代码中,我是能够得到相当接近。最后,我也通过刚才的所有测试套件传递到单人套房的构造,而不是“人工”添加它们,固定我的其他问题固定我的代码。因此,这里是我的解决方案。
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)
是的,这可能更容易只是用鼻子不是要做到这一点,但是这是除了点。
如果你想运行各种测试用例类所有的测试,你很高兴他们指定明确的,那么你可以做这样的:
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)
其中uclid
是我的项目和TestSymbols
和TestPatterns
是TestCase
的子类。
我已经使用了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
我尝试了各种办法,但似乎都存在缺陷,或者有化妆的一些代码,这很烦人。但是有linux下的一个方便易办法,那就是简单的通过特定的模式找到每一个测试,然后通过一个调用它们一个。
find . -name 'Test*py' -exec python '{}' \;
和最重要的是,它肯定工程。
在情况下 打包 库或程序,你不想做到这一点。 setuptools
会为你做它.
使用这种命令,项目的测试必须包裹
unittest
测试通过一种功能,一测试用例类或方法,或者一个模块或包含TestCase
课程。如果命名的套房是一个模块,模块中有一个additional_tests()
功能,它是所谓的结果(这必须是一个unittest.TestSuite
)是加入试验可以运行。如果命名是一套包, 任何子和子包是递归增加到总检验套房.
只是告诉它你根检验,包装,如:
setup(
# ...
test_suite = 'somepkg.test'
)
并运行 python setup.py test
.
文件基于发现可能有问题的Python3,除非你避免的相对进口在你的测试,因为 discover
使用文件导入。即使它支持可选的 top_level_dir
, 但我有一些无限递归的错误。所以一个简单的解决方案的一个非封装码是把以下 __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
我用的PyDev / LiClipse并没有真正想通了如何运行在从GUI一旦所有的测试。 (编辑:你右击根测试文件夹并选择Run as -> Python unit-test
这是我的当前的解决方法:
import unittest
def load_tests(loader, tests, pattern):
return loader.discover('.')
if __name__ == '__main__':
unittest.main()
我把一个模块调用all
这个代码在我的测试目录。如果我运行这个模块从LiClipse一个单元测试,然后所有的测试运行。如果我问只能重复特定的或失败的测试,那么只有那些测试运行。它不与我的命令行测试跑步或者(nosetests)干扰 - 这是忽略
您可能需要更改的参数discover
根据您的项目设置。
根据我加入嵌套测试模块支持斯蒂芬卡格尔的答案。
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)
代码搜索.
文件*Tests.py
的所有子目录然后将其加载。它需要每个*Tests.py
含有其又加载并执行了一个又一个的单个类*Tests(unittest.TestCase)
。
这可与目录的/模块任意深度嵌套的,但在需要之间的每个目录中至少包含一个空__init__.py
文件。这允许测试通过由点替换斜线(或反斜杠)(见replace_slash_by_dot
)加载嵌套模块。
因为测试发现似乎是一个完整的主题,所以有一些专门的框架来测试发现:
更多阅读这里: https://wiki.python.org/moin/PythonTestingToolsTaxonomy
这bash脚本会从任何地方在文件系统中执行单元测试蟒蛇test目录,不管你是在什么工作目录。它的工作目录总是如该test
目录位于
所有测试中,独立$ PWD 强>
单元测试Python模块是当前目录的敏感,除非你告诉它(使用discover -s
选项)。
这在./src
或./example
工作目录时住是有用的,你需要一个快速的整体单元测试:
#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"
python -m unittest discover -s "$readlink"/test -v
<强>选择的测试,独立$ PWD 强>
我命名该实用程序文件:runone.py
并使用它是这样的:
runone.py <test-python-filename-minus-dot-py-fileextension>
#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"
(cd "$dirname"/test; python -m unittest $1)
无需生产过程中test/__init__.py
文件到您的负担包/内存开销。
下面是通过创建一个包装以运行在命令行测试:
#!/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类,所以每个测试的只会是这样的:
from BaseTest import BaseTest
class FooTestPagesBasic(BaseTest):
def test_foo(self):
driver = self.driver
driver.get(self.base_url + "/")
要运行,则简单地指定测试作为的命令行参数部分,e.g:
./run_tests.py -h http://example.com/ tests/**/*.py