簡體   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