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

在我们的团队中,我们定义了大多数测试用例,如下所示:

一门“框架”课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的新手,所以我迷失了方向,不停地尝试除方法之外的所有事情。

In our team, we define most test cases like this:

One “framework” class ourtcfw.py:

import unittest

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

    # other stuff that we want to use everywhere

and a lot of test cases like 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()

When I’m writing new test code and want to run it often, and save time, what I do is that I put “__” in front of all other tests. But it’s cumbersome, distracts me from the code I’m writing and the commit noise this creates is plain annoying.

So e.g. when making changes to testItIsHot(), I want to be able to do this:

$ python testMyCase.py testItIsHot

and have unittest run only testItIsHot()

How can I achieve that?

I tried to rewrite the if __name__ == "__main__": part, but since I’m new to Python, I’m feeling lost and keep bashing into everything else than the methods.


回答 0

这可以按照您的建议进行工作-您只需指定类名即可:

python testMyCase.py MyCase.testItIsHot

This works as you suggest – you just have to specify the class name as well:

python testMyCase.py MyCase.testItIsHot

回答 1

如果您组织测试用例,即遵循与实际代码相同的组织,并且对同一包中的模块使用相对导入

您还可以使用以下命令格式:

python -m unittest mypkg.tests.test_module.TestClass.test_method
# In your case, this would be:
python -m unittest testMyCase.MyCase.testItIsHot

与此相关的Python3文档:https ://docs.python.org/3/library/unittest.html#command-line-interface

If you organize your test cases, that is, follow the same organization like the actual code and also use relative imports for modules in the same package

You can also use the following command format:

python -m unittest mypkg.tests.test_module.TestClass.test_method
# In your case, this would be:
python -m unittest testMyCase.MyCase.testItIsHot

Python3 documentation for this: https://docs.python.org/3/library/unittest.html#command-line-interface


回答 2

您可以猜到它可以很好地工作

python testMyCase.py MyCase.testItIsHot

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

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

It can work well as you guess

python testMyCase.py MyCase.testItIsHot

And there is another way to just test testItIsHot:

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

回答 3

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

它不需要您将a定义unittest.main()为模块的默认行为。

If you check out the help of the unittest module it tells you about several combinations that allow you to run test case classes from a module and test methods from a test case class.

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

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


回答 4

也许对某人会有帮助。如果您只想运行特定类的测试:

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

它在python 3.6中对我有用

Maybe, it will be helpful for somebody. In case you want to run only tests from specific class:

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

It works for me in python 3.6


回答 5

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

或者,您可以使用https://docs.python.org/3/library/unittest.html#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. 单元测试模块在脚本中时,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() )

Inspired by @yarkee I combined it with some of the code I already got. You can also call this from another script, just by calling the function run_unit_tests() without requiring to use the command line, or just call it from the command line with python3 my_test_file.py.

import my_test_file
my_test_file.run_unit_tests()

Sadly this only works for Python 3.3 or superior:

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

Runner code:

#! /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()

Editing the code a little, you can pass an array with all unit tests you would like to call:

...
def run_unit_tests(unit_tests_to_run):
    runner = unittest.TextTestRunner()

    classes = \
    [
        LineBalancingUnitTests,
    ]

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

And another file:

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 )

Alternatively, you can use https://docs.python.org/3/library/unittest.html#load-tests-protocol and define the following method on your test module/file:

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

If you want to limit the execution to one single test file, you just need to set the test discovery pattern to the only file where you defined the load_tests() function.

#! /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() )

References:

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

Alternatively to the last main program example, I came up with the following variation after reading the unittest.main() method implementation:

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

回答 6

TL; DR:这很可能会起作用:

python mypkg/tests/test_module.py MyCase.testItIsHot

说明

  • 便捷的方式

    python mypkg/tests/test_module.py MyCase.testItIsHot

    可以正常工作,但它的潜台词是,您已经在测试文件中(通常在末尾)包含了此常规代码段。

    if __name__ == "__main__":
        unittest.main()
  • 不便的方式

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

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

那么为什么第二种方法不方便?因为手动键入该长的,用点分隔的路径会很麻烦(_ 在此处插入您的身体部位之一)。在第一种方法中,mypkg/tests/test_module.py可以使用现代外壳或编辑器自动完成零件。

PS:如果您认为身体部位在腰部以下,那么您就是一个真实的人。:-)我的意思是说“手指关节”。打字过多对您的关节不利。;-)

TL;DR: This would very likely work:

python mypkg/tests/test_module.py MyCase.testItIsHot

The explanation:

  • 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.

So why does the 2nd method considered inconvenient? Because it would be a pain in the (_ insert one of your body part here _) to type that long, dot-delimited path by hand. While in 1st method, the mypkg/tests/test_module.py part can be auto-completed, either by a modern shell, or by your editor.

PS: If you thought that body part is somewhere below your waist, you are an authentic person. :-) I mean to say “finger joint”. Too much typing would be bad for your joints. ;-)


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。