使用Python unittest,如何创建和使用"可返回测试套件的可调用对象"?

FMc*_*FMc 5 python

我正在学习Python,并且一直在尝试更多地了解Python unittest模块的细节.该文档包括以下内容:

为便于运行测试,我们将在后面看到,这是每个测试模块中提供一个可调用的对象返回预先建立的测试套件是一个好主意:

def suite():
    suite = unittest.TestSuite()
    suite.addTest(WidgetTestCase('testDefaultSize'))
    suite.addTest(WidgetTestCase('testResize'))
    return suite
Run Code Online (Sandbox Code Playgroud)

据我所知,没有解释这样做的目的.另外,我无法弄清楚如何使用这种方法.我尝试了几件事没有成功(除了了解我得到的错误消息):

import unittest

def average(values):
    return sum(values) / len(values)

class MyTestCase(unittest.TestCase):
    def testFoo(self):
        self.assertEqual(average([10,100]),55)

    def testBar(self):
        self.assertEqual(average([11]),11)

    def testBaz(self):
        self.assertEqual(average([20,20]),20)

    def suite():
        suite = unittest.TestSuite()
        suite.addTest(MyTestCase('testFoo'))
        suite.addTest(MyTestCase('testBar'))
        suite.addTest(MyTestCase('testBaz'))
        return suite

if __name__ == '__main__':
    # s = MyTestCase.suite()
    # TypeError: unbound method suite() must be called 
    # with MyTestCase instance as first argument

    # s = MyTestCase.suite(MyTestCase())
    # ValueError: no such test method in <class '__main__.MyTestCase'>: runTest

    # s = MyTestCase.suite(MyTestCase('testFoo'))
    # TypeError: suite() takes no arguments (1 given)
Run Code Online (Sandbox Code Playgroud)

下面的"工作",但似乎尴尬,它要求我改变方法签名suite()为" def suite(self):".

s = MyTestCase('testFoo').suite()
unittest.TextTestRunner().run(s)
Run Code Online (Sandbox Code Playgroud)

Nic*_*zet 6

您获得的第一条错误消息是有意义的,并解释了很多.

print MyTestCase.suite # <unbound method MyTestCase.suite>
Run Code Online (Sandbox Code Playgroud)

不受约束.这意味着除非将其绑定到实例,否则无法调用它.它实际上是相同的MyTestCase.run:

print MyTestCase.run # <unbound method MyTestCase.run>
Run Code Online (Sandbox Code Playgroud)

也许现在你不明白为什么你不能打电话suite,但请暂时把它放在一边.你会试着打电话run给上课吗?就像是:

MyTestCase.run() # ?
Run Code Online (Sandbox Code Playgroud)

可能不对,对吧?写这个没有意义,它不起作用,因为它run是一个实例方法,需要一个self实例来处理.好吧,看起来Python suite以其理解的方式"理解" run,作为一种未绑定的方法.

让我们看看为什么:

如果您尝试将该suite方法放在类范围之外,并将其定义为全局函数,它只是起作用:

import unittest

def average(values):
    return sum(values) / len(values)

class MyTestCase(unittest.TestCase):
    def testFoo(self):
        self.assertEqual(average([10,100]),55)

    def testBar(self):
        self.assertEqual(average([11]),11)

    def testBaz(self):
        self.assertEqual(average([20,20]),20)

def suite():
    suite = unittest.TestSuite()
    suite.addTest(MyTestCase('testFoo'))
    suite.addTest(MyTestCase('testBar'))
    suite.addTest(MyTestCase('testBaz'))
    return suite

print suite() # <unittest.TestSuite tests=[<__main__.MyTestCase testMethod=testFoo>, <__main__.MyTestCase testMethod=testBar>, <__main__.MyTestCase testMethod=testBaz>]>
Run Code Online (Sandbox Code Playgroud)

但是你不希望它超出类范围,因为你想调用它 MyTestCase.suite()

你可能认为既然suite是某种"静态的",或者与实例无关的,那么提出一个self论证是没有意义的,是吗?这是正确的.

但是如果你在Python类中定义一个方法,Python会期望该方法将self参数作为第一个参数.省略self参数不会static自动使您的方法.如果要定义"静态"方法,则必须使用staticmethod装饰器:

@staticmethod
def suite():
    suite = unittest.TestSuite()
    suite.addTest(MyTestCase('testFoo'))
    suite.addTest(MyTestCase('testBar'))
    suite.addTest(MyTestCase('testBaz'))
    return suite
Run Code Online (Sandbox Code Playgroud)

这样Python不会将MyTestCase视为实例方法,而是将其视为函数:

print MyTestCase.suite # <function suite at 0x...>
Run Code Online (Sandbox Code Playgroud)

当然,现在你可以打电话MyTestCase.suite(),这将按预期工作.

if __name__ == '__main__':
    s = MyTestCase.suite()
    unittest.TextTestRunner().run(s) # Ran 3 tests in 0.000s, OK
Run Code Online (Sandbox Code Playgroud)