通过命令行运行unittest.TestCase中的单个测试

394
在我们的团队中,我们通常这样定义大部分测试用例:
一个名为ourtcfw.py的“框架”类:
import unittest

class OurTcFw(unittest.TestCase):
    def setUp:
        # Something

    # Other stuff that we want to use everywhere

还有很多像testMyCase.py这样的测试用例:

import localweather

class MyCase(OurTcFw):

    def testItIsSunny(self):
        self.assertTrue(localweather.sunny)

    def testItIsHot(self):
        self.assertTrue(localweather.temperature > 20)

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

当我编写新的测试代码并经常运行它以节省时间时,我会在其他所有测试前加上"__"。但这很麻烦,使我分心,而且这样做会产生不必要的提交噪声。

例如,当对testItIsHot()进行更改时,我希望能够这样做:

$ python testMyCase.py testItIsHot

我希望只运行testItIsHot(),该怎么做?

我尝试重写if __name__ == "__main__":部分,但由于我是Python的新手,感到很迷茫,总是把其他东西搞错了。

9个回答

460

您所建议的方法是可行的 - 您只需指定类名即可:

python testMyCase.py MyCase.testItIsHot

2
哦,我的天!由于测试需要在Python2.6上运行(99%的时间我可以使用Python2.7测试这些测试本身),我查看了2.6.8文档并错过了很多! :-) - Alois Mahdal
1
刚刚注意到这仅在方法被命名为“test*”时才有效,所以不幸的是它不能用于偶尔运行被重命名为“disabled”的测试。 - Alois Mahdal
12
无法用于子目录中的测试——这是成熟 Python 程序中最常见的情况。 - Tom Swirly
4
@TomSwirly,我现在没法检查,但我想你可以通过在目录(及其子目录,如果有的话)中创建(空的)__init__.py 并调用例如 python test/testMyCase.py test.MyCase.testItIsHot 来完成它。 - Alois Mahdal
3
当我这样做时,没有任何反应。我已经找到了解决方法,但我本来希望这种方法适用于我。 - Joe Flack
显示剩余2条评论

218

如果您按照实际代码相同的组织方式组织测试用例,并且在同一软件包中的模块使用相对导入,那么您还可以使用以下命令格式:

python -m unittest mypkg.tests.test_module.TestClass.test_method

# In your case, this would be:
python -m unittest testMyCase.MyCase.testItIsHot

Python 3文档中的内容:命令行界面


12
这太像 Java 风格了。"long_module_name.SameLongNameAsAClass.test_long_name_beginning_with_test_as_a_convention" ...最好不要像一个理智的测试人员一样将其模块化为套件。 - user7851115

97

它能够像你猜想的那样运作良好

python testMyCase.py MyCase.testItIsHot

还有一种方法可以仅测试 testItIsHot

    suite = unittest.TestSuite()
    suite.addTest(MyCase("testItIsHot"))
    runner = unittest.TextTestRunner()
    runner.run(suite)

19
我发现这个答案的第二部分非常有帮助:我正在使用Eclipse + PyDev编写测试,而且我不想转到命令行! - Giovanni Di Milia
为了补充这个答案,如果你想运行完整的TestCase类,可以省略suite部分,改为:runner = unittest.TextTestRunner(),然后跟着runner.run(unittest.makeSuite(MyCase)) - Shlomo Gottlieb

35

如果你查看unittest模块的帮助,它会告诉你有几种组合可以让你从一个模块运行测试用例类和测试用例类中的测试方法。

python3 -m unittest -h

[...]

Examples:
  python3 -m unittest test_module               - run tests from test_module
  python3 -m unittest module.TestClass          - run tests from module.TestClass
  python3 -m unittest module.Class.test_method  - run specified test method
```lang-none

It does not require you to define a `unittest.main()` as the default behaviour of your module.


2
+1,由于术语对于新学习一门语言的人来说可能会很困惑(而且“用法”甚至非常不一致):运行python -m unittest module_test.TestClass.test_method假定有一个文件module_test.py(从当前目录运行;不需要__init.py__);并且module_test.py包含class TestClass(unittest.TestCase)...,其中包含def test_method(self,...)(这在我使用的Python 2.7.13上也适用)。 - michael

23

简而言之: 这很可能会起作用:

python mypkg/tests/test_module.py MyCase.testItIsHot

说明:

  • The convenient way

      python mypkg/tests/test_module.py MyCase.testItIsHot
    

    would work, but its unspoken assumption is you already have this conventional code snippet inside (typically at the end of) your test file.

    if __name__ == "__main__":
        unittest.main()
    
  • The inconvenient way

      python -m unittest mypkg.tests.test_module.TestClass.test_method
    

    would always work, without requiring you to have that if __name__ == "__main__": unittest.main() code snippet in your test source file.

为什么第二种方法被认为不方便呢?因为手动输入那么长的用点号分隔的路径会让你感到很痛苦。而在第一种方法中,mypkg/tests/test_module.py部分可以由现代shell或编辑器自动完成。


21

如果您只想运行特定类中的测试:

if __name__ == "__main__":
    unittest.main(MyCase())

在我的Python 3.6中它可以工作。


我想只对一个测试进行这样的操作,但到目前为止,我无法让它正常工作。:( 我使用的是Python 3.8版本,即使对整个类进行操作也无效。 - szeitlin
忽略之前的评论,无法删除它。:(这在Python 3.8中对我有效。但有一件事让我感到惊讶,如果找不到你的文件,它不会给出有用的错误提示,只会显示"Ran 0 tests",这让我在一分钟内对错误路径感到困惑。 - szeitlin
哦,我错了。它运行了文件中的所有测试,而不仅仅是一个类。所以它没有按照我想要的方式工作。 - szeitlin

16

我的做法是:

cd project_dir
python -m unittest -v path\to\test\testMyCase.py -k my_test_name

-v用于单元测试的详细日志输出。


10
如果你想直接从脚本中运行测试(例如,从jupyter笔记本),你可以这样做来运行一个测试:
from testMyCase import MyCase
unittest.main(argv=['ignored', '-v', 'MyCase.testItIsHot'], exit=False)

注意:-v是可选的,只用于从测试中获取详细信息。
另一种导入模块的版本:
import testMyCase
unittest.main(argv=['ignored', '-v', 'testMyCase.MyCase.testItIsHot'], exit=False)

1
这对我有用。在我的情况下,我在笔记本单元格中有testCase类,所以我甚至不需要导入它。 - kiril
奇怪。我以为它可以工作,但当我仔细检查时,它运行了所有的测试,而不仅仅是我想选择的那一个。 - szeitlin
@szeitlin 在启动脚本时要小心。例如,我曾经遇到过 PyCharm 默认启动 python unittest C:\your_folder\your_test_file.py 的情况,这会导致所有的测试都被启动。 - ronkov
1
我不是用 PyCharm 来运行这些测试(只是用来写它们!)。最终,我通过使用 TestSuite 方法使其工作了,但我并不喜欢它,因为需要几行代码来尝试运行更少的代码 :P - szeitlin

4

yarkee的启发,我将其与我已有的一些代码结合起来。您还可以从另一个脚本中调用它,只需调用函数run_unit_tests(),无需使用命令行,或者通过命令行调用python3 my_test_file.py

import my_test_file
my_test_file.run_unit_tests()

遗憾的是,这仅适用于Python 3.3或以上版本:

import unittest

class LineBalancingUnitTests(unittest.TestCase):

    @classmethod
    def setUp(self):
        self.maxDiff = None

    def test_it_is_sunny(self):
        self.assertTrue("a" == "a")

    def test_it_is_hot(self):
        self.assertTrue("a" != "b")

跑步者代码:
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import unittest
from .somewhere import LineBalancingUnitTests

def create_suite(classes, unit_tests_to_run):
    suite = unittest.TestSuite()
    unit_tests_to_run_count = len( unit_tests_to_run )

    for _class in classes:
        _object = _class()
        for function_name in dir( _object ):
            if function_name.lower().startswith( "test" ):
                if unit_tests_to_run_count > 0 \
                        and function_name not in unit_tests_to_run:
                    continue
                suite.addTest( _class( function_name ) )
    return suite

def run_unit_tests():
    runner = unittest.TextTestRunner()
    classes =  [
        LineBalancingUnitTests,
    ]

    # Comment all the tests names on this list, to run all Unit Tests
    unit_tests_to_run =  [
        "test_it_is_sunny",
        # "test_it_is_hot",
    ]
    runner.run( create_suite( classes, unit_tests_to_run ) )

if __name__ == "__main__":
    print( "\n\n" )
    run_unit_tests()

编辑代码,稍作修改,您可以传递一个包含所有要调用的单元测试的数组:
...
def run_unit_tests(unit_tests_to_run):
    runner = unittest.TextTestRunner()

    classes = \
    [
        LineBalancingUnitTests,
    ]

    runner.run( suite( classes, unit_tests_to_run ) )
...

还有另一个文件:

import my_test_file

# Comment all the tests names on this list, to run all unit tests
unit_tests_to_run = \
[
    "test_it_is_sunny",
    # "test_it_is_hot",
]

my_test_file.run_unit_tests( unit_tests_to_run )

或者,你可以使用 load_tests Protocol 并在测试模块/文件中定义以下方法:

def load_tests(loader, standard_tests, pattern):
    suite = unittest.TestSuite()

    # To add a single test from this file
    suite.addTest( LineBalancingUnitTests( 'test_it_is_sunny' ) )

    # To add a single test class from this file
    suite.addTests( unittest.TestLoader().loadTestsFromTestCase( LineBalancingUnitTests ) )

    return suite

如果您想将执行限制为单个测试文件,则只需将测试发现模式设置为仅包含定义了load_tests()函数的文件。

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import unittest

test_pattern = 'mytest/module/name.py'
PACKAGE_ROOT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )

loader = unittest.TestLoader()
start_dir = os.path.join( PACKAGE_ROOT_DIRECTORY, 'testing' )

suite = loader.discover( start_dir, test_pattern )
runner = unittest.TextTestRunner( verbosity=2 )
results = runner.run( suite )

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )

sys.exit( not results.wasSuccessful() )

参考资料:

  1. 当unittest模块在脚本中时,sys.argv[1]存在问题
  2. 有没有一种方法可以循环执行Python类中的所有函数?
  3. 在Python中循环遍历类的所有成员变量

除了上一个主程序示例外,我在阅读unittest.main()方法实现后想到了以下变化:

  1. https://github.com/python/cpython/blob/master/Lib/unittest/main.py#L65
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import unittest

PACKAGE_ROOT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )
start_dir = os.path.join( PACKAGE_ROOT_DIRECTORY, 'testing' )

from testing_package import main_unit_tests_module
testNames = ["TestCaseClassName.test_nameHelloWorld"]

loader = unittest.TestLoader()
suite = loader.loadTestsFromNames( testNames, main_unit_tests_module )

runner = unittest.TextTestRunner(verbosity=2)
results = runner.run( suite )

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )
sys.exit( not results.wasSuccessful() )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接