2012-04-11 105 views
18

我想知道我們什麼時候運行unittest.main(),Python如何知道unittest.Testcase有哪些子類?Python的unittest模塊如何檢測測試用例?

例如,如果我添加一個類FromRomanBadInput(unittest.TestCase)unittest知道如何運行此?

+2

元類是涼的東西:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python – Amber 2012-04-11 03:43:23

+0

解讀。非常感謝琥珀! – stupidguy 2012-04-11 04:08:52

+2

@琥珀你是什麼意思? 'unittest'沒有單一的元類。 – bereal 2012-04-11 04:39:06

回答

7

'main'函數搜索所有繼承了導入模塊中unittest.TestCase的類。和電流路徑,然後嘗試運行每個與「測試」

python's document開始方法:

import random 
import unittest 

class TestSequenceFunctions(unittest.TestCase): 

    def setUp(self): 
     self.seq = range(10) 

    def test_shuffle(self): 
     # make sure the shuffled sequence does not lose any elements 
     random.shuffle(self.seq) 
     self.seq.sort() 
     self.assertEqual(self.seq, range(10)) 

     # should raise an exception for an immutable sequence 
     self.assertRaises(TypeError, random.shuffle, (1,2,3)) 

    def test_choice(self): 
     element = random.choice(self.seq) 
     self.assertTrue(element in self.seq) 

    def test_sample(self): 
     with self.assertRaises(ValueError): 
      random.sample(self.seq, 20) 
     for element in random.sample(self.seq, 5): 
      self.assertTrue(element in self.seq) 

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

測試用例被通過繼承創建unittest.TestCase生成。三個 個別測試是用名稱以 字母測試開頭的方法定義的。該命名慣例通知測試運行者有關 哪些方法表示測試。

29

所以我環顧四周,我Python27/Lib目錄...

unittest.main實際上是一個類,unittest.TestProgram的別名。所以會發生什麼是你構造一個這樣的實例,它的運行,它執行一系列的理智檢查和配置,包括動態導入你調用它的模塊(它使用__import__函數,__main__作爲名稱默認情況下要導入的模塊)。所以現在它有一個self.module屬性,它包含一個代表你的源的模塊對象。

最終,它得到這個代碼:

self.test = self.testLoader.loadTestsFromModule(self.module) 

其中self.testLoaderunittest.TestLoader一個實例。該方法包括,除其他的東西:

for name in dir(module): 
     obj = getattr(module, name) 
     if isinstance(obj, type) and issubclass(obj, case.TestCase): 
      tests.append(self.loadTestsFromTestCase(obj)) 

所以它使用你的模塊對象的dir讓你定義(包括類)的全局變量,過濾器的名稱是剛剛從unittest.TestCase派生的類(本地,case.TestCase是這個的別名),然後查找這些類中的測試方法以添加到tests列表中。該搜索的行爲相似:

def isTestMethod(attrname, testCaseClass=testCaseClass, 
        prefix=self.testMethodPrefix): 
     return attrname.startswith(prefix) and \ 
      hasattr(getattr(testCaseClass, attrname), '__call__') 
    testFnNames = filter(isTestMethod, dir(testCaseClass)) 

所以它使用的類dir獲得名嘗試的列表,查找與這些名字的屬性,並選擇那些與self.testMethodPrefix(默認'test')開始,可調用的(具有__call__屬性)。 (我真的很驚訝他們不使用內置的callable函數,我想這是爲了避免拾取嵌套類)

+0

哇。這是非常全面和有益的。感謝您的時間和精力! – stupidguy 2012-04-15 06:21:27

+0

綜合,也許,但我真的不能想象它是如何**有用的**。文檔已經告訴你如何使用**'unittest',這應該是你真正需要知道的。 – 2012-04-16 10:02:32

+1

@KarlKnechtel:呃,碰巧你確實**幫助我解決了這個問題。 :)我爲單元測試和整個測試用例編寫了一個裝飾器,並想知道爲什麼測試運行器不會執行裝飾測試用例。運行上面的代碼,我意識到我忘記了我的裝飾器(不帶參數)將我的unittest.TestCase子類轉換爲完全不同的對象... – balu 2014-05-30 16:10:55

0

我寫了一些代碼,試圖做的行爲類似於unittest.main( )以下。總之,我遍歷模塊,對於不以名稱'unittest'開頭的模塊,我檢查它的成員。然後,如果這些成員是一個類,並且是unittest.TestCase的子類,我通過該類的成員解析。然後,如果這些類的成員是以'test'開頭的函數或方法,我將它添加到測試列表中。類對象的__dict__用於內省方法/函數,因爲使用inspect.getmembers可能會顯示太多。最後,這個測試列表被轉換爲一個元組,幷包裝爲一個套件。然後,該套件以冗長級別2運行。請注意,當然,刪除在函數/方法名稱開頭處檢查'test'的正則表達式可以將bar_test()包括到測試列表中如果你不想要這個限制。

#!/usr/bin/env python 

import unittest 
import inspect 
import sys 
import re 

class Foo(unittest.TestCase): 
    @staticmethod 
    def test_baz(): 
     pass 

    @classmethod 
    def test_mu(cls): 
     pass 

    def test_foo(self): 
     self.assertEqual('foo', 'foo') 

    def bar_test(self): 
     self.assertEqual('bar', 'bar') 

class Bar: 
    pass 

if __name__ == '__main__': 
    runner = unittest.TextTestRunner(verbosity=2) 
    tests = [] 
    is_member_valid_test_class = lambda member: inspect.isclass(member) and \ 
     issubclass(member, unittest.TestCase) 

    for module_name, module_obj in sys.modules.items(): 
     if not re.match(r'unittest', module_name): 
     for cls_name, cls in inspect.getmembers(
      module_obj, is_member_valid_test_class): 
      for methname, methobj in cls.__dict__.items(): 
       if inspect.isroutine(methobj) and re.match(r'test', methname): 
        tests.append(cls(methname)) 

    suite = unittest.TestSuite(tests=tuple(tests)) 
    runner.run(suite) 

產生的輸出是:

test_foo (__main__.Foo) ... ok 
test_baz (__main__.Foo) ... ok 
test_mu (__main__.Foo) ... ok 

---------------------------------------------------------------------- 
Ran 3 tests in 0.001s 

OK