繁体   English   中英

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

[英]With Python unittest, how do I create and use a “callable object that returns a test suite”?

我正在学习Python,并且一直在努力了解Python的unittest模块的细节。 该文档包括以下内容:

为了便于运行测试,我们将在后面看到,最好在每个测试模块中提供一个可调用对象,该对象返回一个预构建的测试套件:

 def suite(): suite = unittest.TestSuite() suite.addTest(WidgetTestCase('testDefaultSize')) suite.addTest(WidgetTestCase('testResize')) return 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

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)

以下“工作”但似乎很尴尬,它要求我将suite()的方法签名更改为' def suite(self): '。

s = MyTestCase('testFoo').suite()
unittest.TextTestRunner().run(s)

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

print MyTestCase.suite # <unbound method MyTestCase.suite>

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

print MyTestCase.run # <unbound method MyTestCase.run>

也许现在你不明白为什么你不能打电话给suite ,但请暂时把它放在一边。 你会尝试调用run的类,像上面? 就像是:

MyTestCase.run() # ?

可能不对,对吧? 写这个是没有意义的,它也行不通,因为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>]>

但是你不希望它超出类范围,因为你想调用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

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

print MyTestCase.suite # <function suite at 0x...>

当然,现在你可以调用MyTestCase.suite() ,这将按预期工作。

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

文档suite()应该是模块中的一个函数,而不是类中的方法。 它似乎只是一个便利功能,因此您以后可以聚合许多模块的测试套件:

alltests = unittest.TestSuite([
  my_module_1.suite(),
  my_module_2.suite(),
])

调用函数的问题都与它在类中的方法有关,但不是作为方法编写的。 self参数是“当前对象”,并且是实例方法所必需的。 (例如ab(1, 2)在概念上与b(a, 1, 2) 。)如果方法对类而不是实例进行操作,请阅读有关classmethod 如果它只是为了方便而与类分组但不对类或实例进行操作,那么请阅读staticmethod 这些都不会特别有助于您使用unittest ,但它们可能有助于解释您为何看到自己所做的事情。

需要注意的一件事是, 鼻子是一种真正简化运行测试的工具。 它允许您准确指定从命令行运行哪些测试,以及遍历每个目录并运行每个测试。

正如已经提到的,文档将suite()称为模块中的方法,而unittest(奇怪地)似乎没有对此方法进行任何特殊识别,因此您必须明确地调用它。 但是,如果使用名为“ testoob ”的工具,它会自动调用suite()方法(如果为main()指定defaultTest =“suite”参数),并在基本unittest包之上添加其他几个功能。 它还提供了生成XML文件的选项,这些文件包括从这些测试中收集的stdout和stderr(这对于自动化测试来说是一大优点),并且还可以生成HTML报告(尽管您需要安装其他软件包)。 我找不到一种方法来自动发现鼻子声称支持的所有测试,所以鼻子可能是更好的选择。

我建议使用以下内容。 这使您无需手动输入随时添加的所有测试。

def suite():
    suite = unittest.TestLoader().loadTestsFromTestCase(Your_Test_Case_Class)
    return suite

这使您可以通过定义以下内容灵活地在同一模块中执行测试:

if __name__ == "__main__":
     suite()

如果你想将套件捆绑在另一个模块中,例如test_suite.py(例如),那么可以通过以下方式完成:

import test_module name import unittest

if __name__=="__main__":
    suite1=test_module_name.suite()
    ...
    ...
    alltests = unittest.TestSuite([suite1,suite2])

现在如何运行测试。 我通常使用更简单的方法在包级别执行此命令来运行,并且unittest自动发现测试:

python -m unittest discover

要么

nosetests 

警告:单元测试比鼻子测试速度快X倍,因此它取决于开发人员的偏好,特别是如果他们使用了第三方鼻子插件并想继续使用它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM