1. 程式人生 > >Python學習筆記__8.3章 單元測試

Python學習筆記__8.3章 單元測試

編程語言 Python

# 這是學習廖雪峰老師python教程的學習筆記

1、概覽

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

比如對函數abs(),我們可以編寫出以下幾個測試用例:

  1. 輸入正數,比如11.20.99,期待返回值與輸入相同;

  2. 輸入負數,比如-1-1.2-0.99,期待返回值與輸入相反;

  3. 輸入0,期待返回0

  4. 輸入非數值類型,比如None[]{},期待拋出TypeError

把上面的測試用例放到一個測試模塊裏,就是一個完整的單元測試。

單元測試的意義:

  • 方便的檢測代碼

  • 在修改代碼的時候,可以極大程度地保證該模塊行為仍然是正確的。

1.1、編寫一個測試單元

  1. 編寫一個Dict類,這個類的行為和dict

    一致,但是可以通過屬性來訪問。

#mydict.py

class Dict(dict):

def __init__(self, **kw): # 初始化實例,可以傳入任意個關鍵字參數,即鍵值對

super().__init__(**kw) # super()無參數,是按mro表,調用父類的方法。這裏是將**kw交給父類dict 初始化

def __getattr__(self, key): # Dict沒有設置初始屬性,所以 Dict()每使用一個屬性,會用到__getattr__方法

try:

return self[key]

# 如果有該屬性,正常執行

except KeyError:

raise AttributeError(r"'Dict' object has no attribute '%s'" % key) # 無該屬性,拋出錯誤

def __setattr__(self, key, value): # 添加鍵值對。即添加屬性

self[key] = value

  1. 編寫單元測試

#mydict_test.py

import unittest

from mydict import Dict

# 繼承的父類是unittest.TestCase

# 它提供了很多內置的條件判斷,我們只需要調用這些方法就可以斷言輸出是否是我們所期望的

class TestDict(unittest.TestCase):

def test_init(self): # 測試Dict能否正常創建實例,並獲取到實例的屬性

d = Dict(a=1, b='test')

self.assertEqual(d.a, 1) #斷言d.a 的返回結果與1相等。相等才能繼續執行

self.assertEqual(d.b, 'test')

self.assertTrue(isinstance(d, dict))

def test_key(self): # Dict() 能否正確添加屬性

d = Dict()

d['key'] = 'value'

self.assertEqual(d.key, 'value')

def test_attr(self): # Dict() 能否正確獲取屬性的value

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): # 獲取不存在的屬性的value值時,期待報錯

value = d['empty']

def test_attrerror(self): # 錯誤測試,同上,獲取屬性value值得方法不同,拋出的錯誤不同

d = Dict()

with self.assertRaises(AttributeError): # 獲取不存在的屬性的value值時,期待報錯

value = d.empty

總結:

  • 以test開頭的方法就是測試方法,不以test開頭的方法不被認為是測試方法,測試的時候不會被執行

  • self.assertEqual(abs(-1), 1) # 斷言函數返回的結果與1相等

  • 期待拋出指定類型的Error,比如通過d['empty']訪問不存在的key時,斷言會拋出KeyError

with self.assertRaises(KeyError):

value = d['empty']

  1. 運行單元測試

運行方式一:

在mydict_test.py的最後加上兩行代碼,這樣可以把mydict_test.py當做正常的python腳本運行

if __name__ == '__main__':

unittest.main()

$ python mydict_test.py

運行方式二:

在命令行通過參數-m unittest直接運行單元測試,這樣可以一次批量運行很多單元測試

$ python -m unittest mydict_test

1.2setUptearDown

setUp()tearDown()方法。會分別在每調用一個測試方法的前後分別被執行

用法:

如果你的測試需要啟動一個數據庫,就可以在setUp()方法中連接數據庫,在tearDown()方法中關閉數據庫,這樣,不必在每個測試方法中重復相同的代碼:

class TestDict(unittest.TestCase):

def setUp(self):

print('setUp...')

def tearDown(self):

print('tearDown...')

2super() 函數

我們在編寫Dict類時。用到了super(),並用其調用了父類dict的方法。在此,我們詳解supper()。

super()的功能就是調用父類函數的方法,並且保證每個父類函數只調用一次(如果每個類都使用super)

2.1super()的入門與使用

在類的繼承中,如果重定義某個方法,該方法會覆蓋父類的同名方法,但有時,我們希望能同時實現父類的功能,這時,我們可以用super()調用父類的方法。

class Animal(object): # 定義了一個Animal

def __init__(self, name):

self.name = name

def greet(self): # 定義了一個greet() 方法

print 'Hello, I am %s.' % self.name

class Dog(Animal): #定義了一個Dog類,父類是Animal

def greet(self): # 也定以了一個greet() 方法,但這個方法是調用Animal的方法

super(Dog, self).greet() # Dog創建的實例名,按mro 表的順序,傳給Dog的下一個父類,調用該父類的方法

print 'WangWang...'

# 調用結果

>>> dog = Dog('dog')

>>> dog.greet()

Hello, I am dog. # 父類方法

WangWang.. # 子類方法

2.2MRO列表

雖然super()調用父類的方法,但卻和父類沒有實質性的關聯。它的調用順序是根據MRO表來的

對於你定義的每一個類,Python 會計算出一個方法解析順序(Method Resolution Order, MRO)列表,它代表了類繼承的順序

>>> Dog.mro() #查看Dog類的 MRO

[<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>]

>>> C.mro() # Base[AB]的父類,[AB]C的父類

[__main__.C, __main__.A, __main__.B, __main__.Base, object]

MRO 列表的順序通過一個 C3 線性化算法來實現的。總的來說,一個類的 MRO 列表就是合並所有父類的 MRO 列表,並遵循以下三條原則:

  • 子類永遠在父類前面

  • 如果有多個父類,會根據它們在列表中的順序被檢查

  • 如果對下一個類存在兩個合法的選擇,選擇第一個父類

2.3super工作原理

super工作原理如下:

def super(cls, inst): # cls表類,instcls創建的實例。獲 cls 在 inst 的 MRO 列表中的下一個類。

mro = inst.__class__.mro() # 獲取instmro

return mro[mro.index(cls) + 1] #返回表中當前cls的下標,並+1,即返回當前cls的下一個父類

Python: 你不知道的 super (http://python.jobbole.com/86787/)


Python學習筆記__8.3章 單元測試