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

Posted

技术标签:

【中文标题】通过命令行从 unittest.TestCase 运行单个测试【英文标题】:Running a single test from unittest.TestCase via the command line 【发布时间】:2013-04-05 00:04:16 【问题描述】:

在我们的团队中,我们会这样定义大多数测试用例:

一个“框架”类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

并让unittest 运行 testItIsHot()

我怎样才能做到这一点?

我试图重写 if __name__ == "__main__": 部分,但由于我是 Python 新手,所以我感到迷茫,并不断抨击方法以外的所有内容。

【问题讨论】:

【参考方案1】:

这正如你所建议的那样工作 - 你只需要指定类名:

python testMyCase.py MyCase.testItIsHot

【讨论】:

天哪!由于测试将在 python2.6 上运行(99% 的时间我可以使用 python2.7 测试测试本身),我正在查看 2.6.8 文档并错过了很多! :-) 刚刚注意到这只有在方法被称为“test*”时才有效,所以不幸的是它不能用于偶尔运行通过重命名“禁用”的测试 不适用于子目录中的测试——这是成熟 Python 程序中最常见的情况。 @TomSwirly 现在无法检查,但我认为您可以通过在该目录(以及子目录,如果有)中创建(空)__init__.py 并调用例如。 python test/testMyCase.py test.MyCase.testItIsHot. 当我这样做时没有任何反应。我找到了解决方法,但我希望这种方法对我有用。【参考方案2】:

如果你组织你的测试用例,也就是按照与实际代码一样的组织,并且对同一个包中的模块也使用相对导入,你也可以使用以下命令格式:

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

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

Python 3 文档:Command-Line Interface

【讨论】:

这是非常笨拙的 Java 风格。 "long_module_name.SameLongNameAsAClass.test_long_name_beginning_with_test_as_a_convention" ...希望您不要像测试代码的理智的人那样模块化成套件。【参考方案3】:

如你所想,它可以很好地工作

python testMyCase.py MyCase.testItIsHot

还有另一种方法来测试testItIsHot

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

【讨论】:

我发现这个答案的第二部分非常有帮助:我正在 Eclipse + PyDev 中编写测试,我不想切换到命令行! 为了补充这个答案,如果您想运行完整的 TestCase 类,您可以省略套件部分并改为:runner = unittest.TextTestRunner() 后跟 runner.run(unittest.makeSuite(MyCase))【参考方案4】:

如果您查看 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.

【讨论】:

+1 并且由于如果是新语言,术语可能会令人困惑(并且 usage 甚至奇怪地不一致):运行 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)【参考方案5】:

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

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

它适用于 Python 3.6。

【讨论】:

【参考方案6】:

TL;DR:这很可能会奏效:

python mypkg/tests/test_module.py MyCase.testItIsHot

解释

方便的方法

  python mypkg/tests/test_module.py MyCase.testItIsHot

会起作用,但是它不言而喻的假设是你已经在你的测试文件中(通常在末尾)有这个传统的代码 sn-p。

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

不方便的方式

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

将始终有效,无需您在测试源文件中包含 if __name__ == "__main__": unittest.main() 代码 sn-p。

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

【讨论】:

【参考方案7】:

灵感来自by 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() )

参考资料:

    Problem with sys.argv[1] when unittest module is in a script Is there a way to loop through and execute all of the functions in a Python class? looping over all member variables of a class in python

另外,对于最后一个主程序示例,我在阅读了unittest.main() 方法实现后想出了以下变体:

    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() )

【讨论】:

【参考方案8】:

如果您想直接从脚本(例如,从 jupyter 笔记本)运行测试,您可以这样做以只运行一个测试:

from testMyCase import MyCase
unittest.main(argv=['ignored', '-v', 'MyCase.testItIsHot'], exit=False)

【讨论】:

成功了,谢谢【参考方案9】:

对我有用的是:

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

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

【讨论】:

以上是关于通过命令行从 unittest.TestCase 运行单个测试的主要内容,如果未能解决你的问题,请参考以下文章

python unittest--TestCase类总结5,skip如何给TestCase增加了__unittest_skip__,__unittest_skip_why__的属性

UnitTest基础测试实例

django.test.TestCase vs unittest vs django.utils.unittest.TestCase 之间的区别

单元测试-unittest

unittest中的testCase执行顺序

鼻子,unittest.TestCase 和元类:未发现自动生成的 test_* 方法