如何在目录中运行所有Python单元测试?

Ste*_*gle 268 python testing unit-testing python-unittest

我有一个包含我的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()
Run Code Online (Sandbox Code Playgroud)

这不起作用,我得到的结果是:

$ python all_test.py 

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK
Run Code Online (Sandbox Code Playgroud)

对于我的第二次尝试,我可以,也许我会尝试以更"手动"的方式完成整个测试.所以我试着在下面这样做:

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()
Run Code Online (Sandbox Code Playgroud)

这也行不通,但似乎太近了!

$ 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 Code Online (Sandbox Code Playgroud)

我似乎有一些类型的套件,我可以执行结果.我有点担心它说我只有run=1,似乎应该是run=2,但这是进步.但是如何将结果传递给main?或者我如何基本上使它工作,所以我可以运行此文件,并在这样做,运行此目录中的所有单元测试?

Tra*_*ear 423

使用Python 2.7及更高版本,您无需编写新代码或使用第三方工具来执行此操作; 通过命令行执行递归测试是内置的.

python -m unittest discover <test_directory>
# or
python -m unittest discover -s <directory> -p '*_test.py'
Run Code Online (Sandbox Code Playgroud)

您可以在python 2.7python 3.x unittest文档中阅读更多内容.

  • 问题包括:ImportError:开始目录不可导入: (11认同)
  • 关于递归:没有<test_directory>的第一个命令默认为"." 并递归到*子模块*.也就是说,您想要发现的所有测试目录都需要__init__.py.如果他们这样做,他们将被discover命令找到.刚尝试过,它有效. (6认同)
  • 至少在Linux上使用Python 2.7.8,命令行调用都没有给我递归.我的项目有几个子项目,其单元测试位于相应的"unit_tests/<subproject>/python /"目录中.如果我指定了这样一个路径,则运行该子项目的单元测试,但只有"unit_tests"作为测试目录参数,没有找到测试(而不是所有子项目的所有测试,正如我所希望的那样).任何提示? (5认同)
  • 谢谢!为什么这不是公认的答案?在我看来,更好的答案总是不需要任何外部依赖的答案...... (5认同)
  • 即使我不指定 -s 和 -p 参数,这也适用于我。只需写“python -m unittest discovery” (2认同)

Ned*_*der 103

您可以使用可以为您执行此操作的测试运行器. 鼻子非常好,例如.运行时,它将在当前树中找到测试并运行它们.

更新:

这是我前鼻子时代的一些代码.您可能不希望显式的模块名称列表,但其余部分可能对您有用.

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)
Run Code Online (Sandbox Code Playgroud)

  • 请注意[鼻子](http://nose.readthedocs.io/en/latest/)已经"处于过去几年的维护模式",目前建议使用[nose2](https:// github. com/nose-devs/nose2),[pytest](http://docs.pytest.org/en/latest/),或者只是普通[unittest](https://docs.python.org/2/library/ unittest.html)/ [unittest2](https://pypi.python.org/pypi/unittest2)用于新项目. (4认同)
  • 这种方法的优点是只是将所有测试模块明确地导入到一个test_all.py模块并调用unittest.main(),您可以选择在某些模块中而不是在其他模块中声明测试套件? (2认同)

tmc*_*ode 68

在python 3中,如果您正在使用unittest.TestCase:

  • 您的目录中必须有一个空(或其他)__init__.py文件test(必须命名test/)
  • 里面的测试文件test/与模式匹配test_*.py.它们可以位于下面的子目录中test/,并且这些子目录可以命名为任何内容.

然后,您可以运行所有测试:

python -m unittest
Run Code Online (Sandbox Code Playgroud)

完成!解决方案少于100行.希望另一个python初学者通过找到它来节省时间.

  • 请注意,默认情况下它只搜索以“test”开头的文件名中的测试 (5认同)
  • 我还需要将 __init__.py 文件添加到每个子文件夹中才能正常工作,否则就很好了。谢谢! (3认同)
  • 您能否更新您的答案以包括子目录也需要是包,以便您需要将 __init__.py 文件添加到测试目录内的子目录中? (3认同)
  • 没错,原始问题涉及“每个单元测试模块的形式为test _ *。py。”的事实,因此,此答案可直接答复。我现在更新了答案,使其更加明确 (2认同)

sla*_*r98 59

现在可以直接从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)
Run Code Online (Sandbox Code Playgroud)

  • 我也试过这种方法,有几个测试,但效果很好.优秀!!!但我很好奇我只有4次测试.它们一起运行0.032s,但当我使用这种方法运行它们时,我得到结果`.... ------------------------- ---------------------------------------------在0.000中进行4次测试好的,为什么?它的区别在哪里? (2认同)
  • 工作完美无缺!只需将其设置在您的 test/ 目录中,然后设置 start_id = "./" 。恕我直言,这个答案现在是(Python 3.7)公认的方式! (2认同)
  • 您可以将最后一行更改为 ´res = runner.run(suite); sys.exit(0 if res.wasSuccessful() else 1)´ 如果您想要正确的退出代码 (2认同)

Ste*_*gle 30

通过研究上面的代码(特别是使用TextTestRunnerdefaultTestLoader),我能够非常接近.最后我通过将所有测试套件传递给单个套件构造函数来修复我的代码,而不是"手动"添加它们,这解决了我的其他问题.所以这是我的解决方案.

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)
Run Code Online (Sandbox Code Playgroud)

是的,使用鼻子可能比做这个更容易,但这是重点.


dem*_*hog 24

如果您想从各种测试用例类中运行所有测试,并且您很乐意明确指定它们,那么您可以这样做:

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)
Run Code Online (Sandbox Code Playgroud)

uclid我的项目在哪里TestSymbols,TestPatterns是的子类TestCase.


rds*_*rds 13

我已经使用了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()
Run Code Online (Sandbox Code Playgroud)

像五个人一样执行

Ran 27 tests in 0.187s
OK
Run Code Online (Sandbox Code Playgroud)


zin*_*ing 7

我尝试了各种各样的方法,但都看起来有缺陷,或者我必须编写一些代码,这很烦人.但是在linux下有一种方便的方法,就是通过某种模式找到每个测试,然后逐个调用它们.

find . -name 'Test*py' -exec python '{}' \;
Run Code Online (Sandbox Code Playgroud)

最重要的是,它绝对有效.


saa*_*aaj 7

对于打包的库或应用程序,您不希望这样做.setuptools 会为你做的.

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

只需告诉它根测试包的位置,例如:

setup(
    # ...
    test_suite = 'somepkg.test'
)
Run Code Online (Sandbox Code Playgroud)

并运行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
Run Code Online (Sandbox Code Playgroud)


Pla*_*ove 5

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

python -m unittest *_test.py
Run Code Online (Sandbox Code Playgroud)

我的所有测试文件都与源文件位于同一文件夹中,并且以_test.