python單元測試之unittest架構使用總結__python

來源:互聯網
上載者:User

一、什麼是單元測試

單元測試是用來對一個模組、一個函數或者一個類來進行正確性檢驗的測試工作。

比如對於函數abs(),我們可以編寫的測試案例為:

(1)輸入正數,比如1、1.2、0.99,期待傳回值與輸入相同

(2)輸入複數,比如-1、-1.2、-0.99,期待傳回值與輸入相反

(3)輸入0,期待返回0

(4)輸入非數實值型別,比如None、[]、{}、期待拋出TypeError

把上面這些測試案例放到一個測試模組裡,就是一個完整的單元測試

 

二、unittest工作原理

unittest中最核心的四部分是:TestCase,TestSuite,TestRunner,TestFixture

(1)一個TestCase的執行個體就是一個測試案例。測試案例就是指一個完整的測試流程,包括測試前準備環境的搭建(setUp),執行測試代碼(run),以及測試後環境的還原(tearDown)。元測試(unit test)的本質也就在這裡,一個測試案例是一個完整的測試單元,通過運行這個測試單元,可以對某一個問題進行驗證。

(2)而多個測試案例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。

(3)TestLoader是用來載入TestCase到TestSuite中的。

(4)TextTestRunner是來執行測試案例的,其中的run(test)會執行TestSuite/TestCase中的run(result)方法

(5)測試的結果會儲存到TextTestResult執行個體中,包括運行了多少測試案例,成功了多少,失敗了多少等資訊。

 

綜上,整個流程就是首先要寫好TestCase,然後由TestLoader載入TestCase到TestSuite,然後由TextTestRunner來運行TestSuite,啟動並執行結果儲存在TextTestResult中,整個過程整合在unittest.main模組中。

 

三、下面舉兩個執行個體,來看看unittest如何測試一個簡單的函數

(1)編寫一個Dict類,這個類的行為和dict一致,但是可以通過屬性來訪問例如

>>> d = Dict(a=1, b=2)>>> d['a']1>>> d.a1

mydict.py代碼如下:

class Dict(dict):    def __init__(self, **kw):        super(Dict, self).__init__(**kw)     def __getattr__(self, key):        try:            return self[key]        except KeyError:            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)     def __setattr__(self, key, value):        self[key] = value 


用於測試的檔案mydict_test.py代碼如下:

import unittestfrom mydict import Dict  class TestDict(unittest.TestCase):    def test_init(self):        d = Dict(a=1, b='test')        self.assertEqual(d.a, 1)  # 判斷d.a是否等於1        self.assertEqual(d.b, 'test')  # 判斷d.b是否等於test        self.assertTrue(isinstance(d, dict))  # 判斷d是否是dict類型     def test_key(self):        d = Dict()        d['key'] = 'value'        self.assertEqual(d.key, 'value')     def test_attr(self):        d = Dict()        d.key = 'value'        self.assertTrue('key' in d)        self.assertEqual(d['key'], 'value')     def test_keyerror(self):        d = Dict()        with self.assertRaises(KeyError):  # 通過d['empty']訪問不存在的key時,斷言會拋出keyerror            value = d['empty']     def test_attrerror(self):        d = Dict()        with self.assertRaises(AttributeError):  # 通過d.empty訪問不存在的key時,我們期待拋出AttributeError            value = d.empty  if __name__ == '__main__':    unittest.main()


直接把mydict_test.py當普通的Python指令碼運行即可

輸出:

.....----------------------------------------------------------------------Ran 5 tests in 0.000s OK


(2)測一個簡單的加減乘除介面

mathfunc.py檔案代碼如下:

def add(a, b):    return a + b def minus(a, b):    return a - b def multi(a, b):    return a * b def divide(a, b):    return a / b

test_mathfunc.py檔案代碼如下:

import unittestfrom mathfunc import *  class TestMathFunc(unittest.TestCase):     def test_add(self):        self.assertEqual(3, add(1, 2))        self.assertNotEqual(3, add(2, 2))     def test_minus(self):        self.assertEqual(1, minus(3, 2))     def test_multi(self):        self.assertEqual(6, multi(3, 2))     def test_divide(self):        self.assertEqual(2, divide(6, 3))        self.assertEqual(2.5, divide(5, 2)) if __name__ == '__main__':unittest.main()

輸出:

.F..======================================================================FAIL: test_divide (__main__.TestDict)----------------------------------------------------------------------Traceback (most recent call last):  File "D:/pythonWorkspace/test_mathfunc.py", line 20, in test_divide    self.assertEqual(2.5, divide(5, 2))AssertionError: 2.5 != 2 ----------------------------------------------------------------------Ran 4 tests in 0.000s FAILED (failures=1)

可以看到一共運行了4個測試,失敗了1個,並且給出了失敗原因,2.5!=2,也就是說我們的divide方法是有問題的。

 

關於輸出的幾點說明:

1、在第一行給出了每一個用例執行的結果的標識,成功是.,失敗是F,出錯是E,跳過是S。從上面可以看出,測試的執行跟方法的順序沒有關係,divide方法寫在了第4個,但是卻在第2個執行。

2、每個測試方法均以test開頭,否則不能被unittest識別

3、在uniitest.main()中加verbosity參數可以控制輸出的錯誤報表的詳細程度,預設是1,如果設為0, 則不輸出每一用例的執行結果,即沒有上面的結果中的第1行,如果設為2,則輸出詳細的執行結果,如下所示:

 

test_add (__main__.TestMathFunc) ... oktest_divide (__main__.TestMathFunc) ... FAILtest_minus (__main__.TestMathFunc) ... oktest_multi (__main__.TestMathFunc) ... ok ======================================================================FAIL: test_divide (__main__.TestMathFunc)----------------------------------------------------------------------Traceback (most recent call last):  File "D:/pythonWorkspace/test_mathfunc.py", line 20, in test_divide    self.assertEqual(2.5, divide(5, 2))AssertionError: 2.5 != 2 ----------------------------------------------------------------------Ran 4 tests in 0.000s FAILED (failures=1)


四、組織TestSuite

上面的測試案例在執行的時候沒有按照順序執行,如果想要讓用例按照你設定的順序執行就用到了TestSuite。我們添加到TestSuite中的case是會按照添加的順序執行的。

現在我們只有一個測試檔案,如果有多個測試檔案,也可以用TestSuite組織起來。

繼續上面第二加減乘除的例子,現在再建立一個檔案,test_suite.py,代碼如下:

# coding=utf-8import unittestfrom test_mathfunc import TestMathFunc if __name__ == '__main__':    suite = unittest.TestSuite()     tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]    suite.addTests(tests)     runner = unittest.TextTestRunner(verbosity=2)    runner.run(suite)

執行結果如下:

test_add (test_mathfunc.TestMathFunc) ... oktest_minus (test_mathfunc.TestMathFunc) ... oktest_divide (test_mathfunc.TestMathFunc) ... FAIL ======================================================================FAIL: test_divide (test_mathfunc.TestMathFunc)----------------------------------------------------------------------Traceback (most recent call last):  File "D:\pythonWorkspace\HTMLTest\test_mathfunc.py", line 20, in test_divide    self.assertEqual(2.5, divide(5, 2))AssertionError: 2.5 != 2 ----------------------------------------------------------------------Ran 3 tests in 0.000s FAILED (failures=1)

五、將結果輸出到檔案

現在我們的測試結果只能輸出到控制台,現在我們想將結果輸出到檔案中以便後續可以查看。

將test_suite.py進行一點修改,代碼如下:

# coding=utf-8 import unittestfrom test_mathfunc import TestMathFunc if __name__ == '__main__':    suite = unittest.TestSuite()     tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]    suite.addTests(tests)     with open('UnittestTextReport.txt', 'a') as  f:        runner = unittest.TextTestRunner(stream=f, verbosity=2)        runner.run(suite) 

運行該檔案,就會發現目錄下產生了'UnittestTextReport.txt,所有的執行報告均輸出到了此檔案中。

 

六、test fixture的setUp和tearDown

當遇到要啟動一個資料庫這種情況時,只想在開始時串連上資料庫,在結束時關閉串連。那麼可以使用setUp和tearDown函數。

class TestDict(unittest.TestCase):     def setUp(self):        print 'setUp...'     def tearDown(self):        print 'tearDown...'


這兩個方法在每個測試方法執行前以及執行後執行一次,setUp用來為測試準備環境,tearDown用來清理環境,以備後續的測試。

 

如果想要在所有case執行之前準備一次環境,並在所有case執行結束之後再清理環境,我們可以用setUpClass()與tearDownClass(),代碼格式如下:

class TestMathFunc(unittest.TestCase):    @classmethod    def setUpClass(cls):        print "setUp"     @classmethod    def tearDownClass(cls):        print "tearDown"


七、跳過某個case

unittest提供了幾種方法可以跳過case

(1)skip裝飾器

 

代碼如下

# coding=utf-8import unittestfrom mathfunc import * class TestMathFunc(unittest.TestCase):      .....     @unittest.skip("i don't want to run this case.")    def test_minus(self):        self.assertEqual(1, minus(3, 2))


輸出:

test_add (test_mathfunc.TestMathFunc) ... oktest_minus (test_mathfunc.TestMathFunc) ... skipped "i don't want to run this case."test_divide (test_mathfunc.TestMathFunc) ... FAIL ======================================================================FAIL: test_divide (test_mathfunc.TestMathFunc)----------------------------------------------------------------------Traceback (most recent call last):  File "D:\pythonWorkspace\HTMLTest\test_mathfunc.py", line 28, in test_divide    self.assertEqual(2.5, divide(5, 2))AssertionError: 2.5 != 2 ----------------------------------------------------------------------Ran 3 tests in 0.000s FAILED (failures=1, skipped=1)

skip裝飾器一共有三個

unittest,skip(reason):無條件跳過

unittest.skipIf(condition, reason):當condition為True時跳過

unittest.skipUnless(condition, reason):當condition為False時跳過


(2)TestCase.skipTest()方法

 

class TestMathFunc(unittest.TestCase):...def test_minus(self):        self.skipTest('do not run this.')        self.assertEqual(1, minus(3, 2))


輸出:

test_add (test_mathfunc.TestMathFunc) ... oktest_minus (test_mathfunc.TestMathFunc) ... skipped 'do not run this.'test_divide (test_mathfunc.TestMathFunc) ... FAIL ======================================================================FAIL: test_divide (test_mathfunc.TestMathFunc)----------------------------------------------------------------------Traceback (most recent call last):  File "D:\pythonWorkspace\HTMLTest\test_mathfunc.py", line 20, in test_divide    self.assertEqual(2.5, divide(5, 2))AssertionError: 2.5 != 2 ----------------------------------------------------------------------Ran 3 tests in 0.000s FAILED (failures=1, skipped=1)


八、用HTMLTestRunner輸出漂亮的HTML報告

txt格式的文本執行報告過於簡陋,這裡我們學習一下藉助HTMLTestRunner產生HTML報告。首先需要下載HTMLTestRunner.py,並放到目前的目錄下,或者python目錄下的Lib中,就可以匯入運行了。

下載地址:http://tungwaiyip.info/software/HTMLTestRunner.html

 

將test_suite.py代碼修改如下:

# coding=utf-8 import unittestfrom test_mathfunc import TestMathFuncfrom HTMLTestRunner import HTMLTestRunner  if __name__ == '__main__':    suite = unittest.TestSuite()     tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]    suite.addTests(tests)     with open('HTMLReport.html', 'w') as f:        runner = HTMLTestRunner(stream=f,                                title = 'MathFunc Test Report',                                description='generated by HTMLTestRunner.',                                verbosity=2                                )        runner.run(suite)


執行後,控制台輸出如下:

ok test_add (test_mathfunc.TestMathFunc)F  test_divide (test_mathfunc.TestMathFunc) Time Elapsed: 0:00:00.001000

產生的html:

 

 

九、總結

1、unittest是python內建的單元測試架構,我們可以用其來作為我們自動化測試架構的用例組織執行架構。

2、unittest的流程:寫好TestCase,然後由TestLoader載入TestCase到TestSuite,然後由TextTestRunner來運行TestSuite,啟動並執行結果儲存在TextTestResult中,我們通過命令列或者unittest.main()執行時,main會調用TextTestRunner中的run來執行,或者我們可以直接通過TextTestRunner來執行用例。

3、一個class繼承unittest.TestCase即是一個TestCase,其中以 test 開頭的方法在load時被載入為一個真正的TestCase。

4、verbosity參數可以控制執行結果的輸出,0 是簡單報告、1 是一般報告、2 是詳細報告。

5、可以通過addTest和addTests向suite中添加case或suite,可以用TestLoader的loadTestsFrom__()方法。

6、用 setUp()、tearDown()、setUpClass()以及 tearDownClass()可以在用例執行前布置環境,以及在用例執行後清理環境

7、我們可以通過skip,skipIf,skipUnless裝飾器跳過某個case,或者用TestCase.skipTest方法。

8、參數中加stream,可以將報告輸出到檔案:可以用TextTestRunner輸出txt報告,以及可以用HTMLTestRunner輸出html報告。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.