假設如今系統有兩個模型A和B,其中A依賴B(例如A,B都是函數,A函數體內調用了B函數),可是B還沒完成,或者根本就不在控制以內;html
這時候又須要對A的功能進行單獨測試,就須要使用mock對象,模擬出一個假的fake_B模塊,雖然這個fake_B模塊是假的,可是咱們能夠經過對它的行爲進行定製來使他可以看起來「像」B模塊的功能,使A依賴fake_B,來對A的功能進行測試。python
同時,因爲fake_B是徹底可控的,除了能夠定製B的屬性,返回值以外,還能夠對B模塊的使用狀況進行測試。ide
而在Python中可使用mock和unittest.mock來產生這樣的mock對象。函數
首先須要建立一個mock對象:測試
>>>from mock import MagicMock >>>fake_obj = MagicMock()
而後能夠經過return_value設定它的返回值,當你調用這個mock對象時返回,返回值能夠是任何類型,變量,函數,類對象均可以。code
>>>fake_obj.return_value = 'This is a mock object' >>>fake_obj() 'This is a mock object'
也能夠經過side_effect指定它的反作用,這個反作用就是當你調用這個mock對象是會調用的函數,也能夠選擇拋出一個異常,來對程序的錯誤狀態進行測試。orm
>>>def b(): ... print 'This is b' ... >>>fake_obj.side_effect = b >>>fake_obj() This is b >>>fake_obj.side_effect = KeyError('This is b') >>>fake_obj() ... KeyError: 'This is b'
同時也能夠以一個對象base爲基礎,拓展mock,使得mock得到base的全部屬性和方法(但僅是接口相同,沒有實現),當調用不屬於base的屬性和方法時,會拋出AttributeError。這須要在建立mock對象的時候,在spec參數指定,前面的返回值和反作用也能夠在建立時進行指定。htm
>>>class B: ... def __init__(self): ... self.a = 'a' ... >>>b = B() >>>fake_obj = MagicMock(b) <MagicMock spec='B' id='4370614160'> >>>fake_obj.a 'a' >>>fake_obj.b AttributeError: Mock object has no attribute 'b'
還有一點須要強調的就是,若是要模擬一個對象而不是函數,你能夠直接在mock對象上添加屬性和方法,而且每個添加的屬性都是一個mock對象,也就是說能夠對這些屬性進行配置,而且能夠一直遞歸的定義下去。對象
>>>fake_obj.fake_a.return_value = 'This is fake_obj.fake_a' >>>fake_obj.fake_a() 'This is fake_obj.fake_a'
在對mock對象進行必定的配置以後就是使用mock對象進行測試,這個模塊採用的‘action-assertion’的方式進行測試,即先運行,再經過斷言進行測試。例如能夠測試mock對象是否被調用,調用了幾回,如何被調用等等,mock對象自己提供了豐富的斷言方法,官方文檔中提供了詳盡的描述。blog
>>>fake_obj = MagicMock(return_value = 1) >>>fake_obj.assert_called() AssertionError: Expected 'None' to have been called. >>>fake_obj() >>>fake_obj.assert_called() >>> >>>fake_obj(1,2,3,key=1) >>>fake_obj.assert_called_with(1,2,3,key=1) >>>
同時mock庫提供了patch函數來簡化mock對象對原對象的替換。patch能夠做爲裝飾器或者上下文管理器使用,這意味着在裝飾的函數和上下文管理器中,對應的類會被替換爲mock對象。
>>>from mock import patch >>>@patch('__main__.SomeClass') ... def function(normal_argument, mock_class): ... print(mock_class is SomeClass) ... >>>function(None) True