我有一个包含Python单元测试的目录。每个单元测试模块的格式是test_*.py。我正在尝试创建一个名为all_test.py的文件,您猜对了,它将以上述测试形式运行所有文件并返回结果。到目前为止,我尝试了两种方法;两者都失败了。我将展示这两种方法,我希望有人知道如何正确地做到这一点。

对于我的第一次勇敢的尝试,我想“如果我只是导入文件中的所有测试模块,然后调用这个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

我似乎有一个某种类型的套件,我可以执行结果。我有点担心这个事实,它说我只运行=1,似乎应该运行=2,但它是进步。但是我如何传递和显示结果的主要?或者说,我怎样才能让它工作,从而运行这个文件,并运行这个目录下的所有单元测试?


当前回答

使用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。X单元测试文档。


2021年更新:

许多现代python项目使用更高级的工具,如pytest。例如,下拉matplotlib或scikit-learn,您将看到它们都在使用它。

了解这些新工具非常重要,因为当您有超过7000个测试时,您需要:

更高级的方法来总结通过、跳过、警告和错误的内容 很容易看出他们是如何失败的 运行时的完成百分比 总运行时间 生成测试报告的方法 等等

其他回答

遇到了同样的问题。

解决方案是在每个文件夹中添加一个空的__init__.py,并使用python -m unittest discover -s

项目结构

tests/
  __init__.py
  domain/
    value_object/
      __init__.py
      test_name.py
    __init__.py
  presentation/
    __init__.py
    test_app.py

然后运行命令

python -m unittest discover -s tests/domain

得到预期的结果

.
----------------------------------------------------------------------
Ran 1 test in 0.007s

这是一个老问题,但现在(2019年)对我有用的是:

python -m unittest *_test.py

我的所有测试文件都在与源文件相同的文件夹中,它们以_test结尾。

我使用了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

我使用PyDev/LiClipse,还没有真正弄清楚如何从GUI一次运行所有测试。(编辑:右键单击根测试文件夹并选择运行为-> Python unit-test

这是我目前的解决方案:

import unittest

def load_tests(loader, tests, pattern):
    return loader.discover('.')

if __name__ == '__main__':
    unittest.main()

我把这些代码放在一个名为all in我的测试目录的模块中。如果我从LiClipse运行这个模块作为单元测试,那么所有测试都将运行。如果我要求只重复特定或失败的测试,则只运行这些测试。它也不会干扰我的命令行测试运行器(nosetests)——它被忽略了。

您可能需要根据项目设置更改要发现的参数。

对于打包的库或应用程序,您不希望这样做。Setuptools将为您做这件事。

要使用此命令,项目的测试必须由函数、TestCase类或方法或包含TestCase类的模块或包包装在unittest测试套件中。如果命名套件是一个模块,并且该模块有一个additional_tests()函数,则调用该函数,并将结果(必须是unittest.TestSuite)添加到要运行的测试中。如果命名套件是一个包,那么任何子模块和子包都会递归地添加到整个测试套件中。

只需要告诉它你的根测试包在哪里,比如:

setup(
    # ...
    test_suite = 'somepkg.test'
)

然后运行python setup.py test。

在Python 3中,基于文件的发现可能会有问题,除非您在测试套件中避免相对导入,因为发现使用文件导入。尽管它支持可选的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