第一次英勇尝试,我想:“如果我将所有测试模块导入文件中,然后将其称为
unittest.main()
doodad,它将起作用,对吗?”好吧,原来我错了。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
,但这是进步。但是如何传递结果并将其显示给main?还是我基本上如何使其工作,以便我可以运行该文件,然后运行此目录中的所有单元测试?#1 楼
使用Python 2.7和更高版本,您无需编写新代码或使用第三方工具即可完成此操作。内置了通过命令行执行递归测试的功能。将__init__.py
放入您的测试目录中,然后:python -m unittest discover <test_directory>
# or
python -m unittest discover -s <directory> -p '*_test.py'
您可以在python 2.7
或python 3.x unittest文档中阅读更多内容。
评论
问题包括:ImportError:起始目录不可导入:
–缩放
13年5月5日在2:25
至少在Linux上使用Python 2.7.8时,命令行调用都不给我递归。我的项目有几个子项目,这些子项目的单元测试分别位于“ unit_tests /
–user686249
15年7月15日在14:30
关于递归:第一个没有
– EmilStenström
16年5月5日在12:45
这对我有用。我有一个包含四个文件的测试文件夹,可以在我的Linux终端上运行,非常棒。
–JasTonAChair
16-09-22在9:42
谢谢!为什么这不是公认的答案?我认为,更好的答案永远是不需要任何外部依赖项的答案。
–乔纳森·本恩
17年9月26日在13:02
#2 楼
在python 3中,如果您使用的是unittest.TestCase
:您的
__init__.py
目录中必须有一个空的(或其他)test
文件(必须命名为test/
)您的测试
test/
中的文件与模式test_*.py
相匹配。它们可以位于test/
下的子目录中,并且这些子目录可以命名为任意名称。然后,您可以使用以下命令运行所有测试:
python -m unittest
完成!解决方案少于100行。希望其他python初学者可以通过查找此方法节省时间。
评论
请注意,默认情况下,它仅以“ test”开头的文件名搜索测试
– Shawabawa
18/12/6在14:58
没错,原始问题涉及“每个单元测试模块的形式为test _ *。py。”的事实,因此,此答案可直接答复。我现在更新了答案,使其更加明确
– tmck代码
19年1月24日在1:01
谢谢,这是我使用Travis Bear的答案所缺少的。
– Jeremy Cochoy
19/12/7在15:42
#3 楼
您可以使用测试运行程序来为您执行此操作。例如鼻子很好。运行时,它将在当前树中找到测试并运行它们。更新时间:
这是我前鼻时期的一些代码。您可能不需要明确的模块名称列表,但其余的模块可能对您有用。
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)
评论
与仅将所有测试模块显式导入一个test_all.py模块并调用unittest.main()相比,这种方法的优势是您可以选择在某些模块中声明测试套件,而在其他模块中声明测试套件吗?
– Corey Porter
09年11月13日23:50
我试了一下鼻子,效果很好。在我的项目中安装和运行都很容易。我什至可以在virtualenv内运行几行脚本来实现自动化。鼻子+1!
–杰西·韦伯(Jesse Webb)
2012年1月5日18:59
并非总是可行的:有时,如果尝试在模块上运行导入,则项目的导入结构可能导致混乱。
– chiffa
15年11月20日在13:47
请注意,鼻子已经“在过去几年中处于维护模式”,目前建议对新项目使用鼻子2,pytest或仅使用普通unittest / unittest2。
–库尔特·皮克
17年1月11日在10:45
您是否曾经尝试从测试实例对象运行测试?
–木偶奇遇记
17年6月24日在23:16
#4 楼
现在可以直接从unittest中实现:unittest.TestLoader.discover。import unittest
loader = unittest.TestLoader()
start_dir = 'path/to/your/test/files'
suite = loader.discover(start_dir)
runner = unittest.TextTestRunner()
runner.run(suite)
评论
我也尝试过这种方法,进行了几次测试,但是效果很好。优秀的!!!但是我很好奇我只有4个测试。它们一起运行0.032s,但是当我使用此方法全部运行它们时,我得到结果.... -------------------------- --------------------------------------------在0.000秒内进行了4次测试好为什么区别,它来自哪里?
– Simkusr
18-4-22在5:58
我无法从命令行运行看起来像这样的文件。应该如何调用它?
–达斯汀·米歇尔(Dustin Michels)
18-09-18在20:18
python file.py
–slaughter98
18-09-19在2:21
完美地工作!只需在您的test /目录中进行设置,然后设置start_id =“ ./”即可。恕我直言,这个答案现在(Python 3.7)已被接受!
– jjwdesign
19年6月17日在0:57
您可以将最后一行更改为´res = Runner.run(suite); sys.exit(如果res.wasSuccessful()则为0,否则为1)´,如果您想要正确的退出代码
–萨达普
20-04-18在8:07
#5 楼
通过研究上面的代码(特别是使用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)
是的,仅使用鼻子比这样做更容易,但这并不重要。
评论
好,它对于当前目录工作正常,如何直接调用子目录?
–蔡礼
13年1月8日在6:00
拉里(Larry),有关递归测试发现,请参见新答案(stackoverflow.com/a/24562019/104143)
– Peter Kofler
2014年7月3日在19:38
您是否曾经尝试从测试实例对象运行测试?
–木偶奇遇记
17年6月24日在23:17
#6 楼
如果要运行各种测试用例类中的所有测试,并且希望明确地指定它们,则可以这样做: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
的子类。评论
来自unittest.TestLoader文档:“通常,无需创建此类的实例; unittest模块提供了一个可以作为unittest.defaultTestLoader共享的实例。”同样,由于TestSuite接受iterable作为参数,因此可以在循环中构造该iterable以避免重复loader.loadTestsFromTestCase。
–两位炼金术士
15年3月17日在23:11
@ Two-Bit Alchemist,您的第二点特别好。我将代码更改为包含,但无法对其进行测试。 (虽然我意识到我很不理智(给他们一个骆驼大小写变量名称),但第一个mod会让它看起来像Java。.
–痴呆的刺猬
16年2月12日在4:52
这是我的最爱,很干净。能够将其打包并在我的常规命令行中将其作为参数。
– MarkII
16-10-22在20:13
#7 楼
我使用了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
评论
我想这仅适用于python2.7
–蔡礼
13年1月8日在5:41
@larrycai也许,我通常使用Python 3,有时使用Python 2.7。该问题与特定版本无关。
–rds
13年1月8日在9:14
我使用的是Python 3.4,发现返回了一个套件,从而使循环变得多余。
–沙丘
2014年7月22日14:12
对于将来的Larry来说:“ Python 2.7在unittest中添加了许多新功能,包括测试发现。unittest2允许您将这些功能与Python的早期版本一起使用。”
–两位炼金术士
15年3月17日在23:48
#8 楼
我尝试了各种方法,但似乎都存在缺陷,或者我必须编写一些代码,这很烦人。但是在Linux下,有一种简便的方法,就是通过某种模式找到每个测试,然后逐个调用它们。find . -name 'Test*py' -exec python '{}' \;
,最重要的是,它肯定可以工作。
#9 楼
如果是打包的库或应用程序,则不想这样做。setuptools
会为您做。 要使用此命令,必须使用函数,TestCase类或方法或包含
unittest
类的模块或程序包将项目的测试包装在TestCase
测试套件中。如果命名套件是一个模块,并且该模块具有additional_tests()
函数,则会调用它,并将结果(必须是unittest.TestSuite
)添加到要运行的测试中。如果命名套件是一个包,则将所有子模块和子包都递归添加到整个测试套件中。只需告诉它您的根测试包在哪里,例如:
setup(
# ...
test_suite = 'somepkg.test'
)
并运行
python setup.py test
。在Python 3中基于文件的发现可能会出现问题,除非您避免在测试套件中进行相对导入,因为
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
评论
很好的答案,它可以在部署之前用于自动测试!谢谢
– Arthur Clerc-Gherardi
19年11月25日在9:56
#10 楼
我使用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运行此模块,则将运行所有测试;如果我仅要求重复特定或失败的测试,则仅运行那些测试。这不会干扰我的命令行测试要么成为(noestests),要么被忽略。您可能需要根据项目设置将参数更改为
discover
。评论
所有测试文件和测试方法的名称均应以“ test_”开头。否则,命令“运行方式-> Python单元测试”将找不到它们。
– Stefan
17年9月7日在12:39
#11 楼
这是一个老问题,但现在(2019年)对我有用的是:python -m unittest *_test.py
我所有的测试文件都与源文件位于同一文件夹中,以
_test
结尾。#12 楼
根据Stephen Cagle的回答,我添加了对嵌套测试模块的支持。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
)。#13 楼
因为测试发现似乎是一个完整的主题,所以有一些专用的框架来测试发现:nose
Py.Test
在这里阅读更多:https://wiki.python.org/moin/PythonTestingToolsTaxonomy
#14 楼
无论您位于哪个工作目录中,此BASH脚本都会从文件系统中的任何位置执行python unittest测试目录:其工作目录始终位于test
目录所在的位置。所有测试,独立$ PWD
unittest Python模块对您当前的目录敏感,除非您告诉它当前位置(使用
discover -s
选项)。在保持
./src
或./example
正常工作时这很有用目录,您需要进行快速的整体单元测试: #!/bin/bash
this_program="runone.py
"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"
python -m unittest discover -s "$readlink"/test -v
选定的测试,独立的$ PWD
我将此实用程序文件命名为:
runone.py <test-python-filename-minus-dot-py-fileextension>
并像这样使用它: #!/bin/bash
this_program="test/__init__.py
"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"
(cd "$dirname"/test; python -m unittest )
q4312079q
不需要一个q4312079q文件会在生产过程中加重您的包装/内存开销。
#15 楼
我只是在基本测试目录中创建了一个discover.py文件,并为子目录中的任何内容添加了import语句。然后discover可以通过在discover.py上运行它来在这些目录中找到我所有的测试。
python -m unittest discover ./test -p '*.py'
# /test/discover.py
import unittest
from test.package1.mod1 import XYZTest
from test.package1.package2.mod2 import ABCTest
...
if __name__ == "__main__"
unittest.main()
#16 楼
这是我创建包装程序以从命令行运行测试的方法:#!/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 + "/")
要运行,您只需将测试指定为命令行参数的一部分,例如:
./run_tests.py -h http://example.com/ tests/**/*.py
评论
大多数答案与测试发现无关(即日志记录等)。堆栈溢出用于回答问题,而不是炫耀不相关的代码。
– Corey Goldberg
17年1月19日在4:29
评论
如果您使用的是Python 2.7+,请跳至Travis的答案您是否曾经尝试从测试实例对象运行测试?
有关示例文件结构的解决方案,请参见此答案。