在這個(gè)例子中,我們甚至不需要補充任何功能,只需創(chuàng )建一個(gè)帶auto-spec方法的RemovalService類(lèi),然后將該實(shí)例注入到UploadService中對方法驗證。
mock.create_autospec為類(lèi)提供了一個(gè)同等功能實(shí)例。這意味著(zhù),實(shí)際上來(lái)說(shuō),在使用返回的實(shí)例進(jìn)行交互的時(shí)候,如果使用了非法的方法將會(huì )引發(fā)異常。更具體地說(shuō),如果一個(gè)方法被調用時(shí)的參數數目不正確,將引發(fā)一個(gè)異常。這對于重構來(lái)說(shuō)是非常重要。當一個(gè)庫發(fā)生變化的時(shí)候,中斷測試正是所期望的。如果不使用auto-spec,即使底層的實(shí)現已經(jīng)破壞,我們的測試仍然會(huì )通過(guò)。
陷阱:mock.Mock和mock.MagicMock類(lèi)
mock庫包含兩個(gè)重要的類(lèi)mock.Mock和mock.MagicMock,大多數內部函數都是建立在這兩個(gè)類(lèi)之上的。在選擇使用mock.Mock實(shí)例,mock.MagicMock實(shí)例或auto-spec方法的時(shí)候,通常傾向于選擇使用 auto-spec方法,因為它能夠對未來(lái)的變化保持測試的合理性。這是因為mock.Mock和mock.MagicMock會(huì )無(wú)視底層的API,接受所有的方法調用和參數賦值。比如下面這個(gè)用例:
1 2 3 4 |
class Target(object): def apply(value): return valuedef method(target, value): return target.apply(value) |
我們像下面這樣使用mock.Mock實(shí)例來(lái)做測試:
1 2 3 4 5 6 7 8 |
class MethodTestCase(unittest.TestCase): def test_method(self): target = mock.Mock() method(target, "value") target.apply.assert_called_with("value") |
這個(gè)邏輯看似合理,但如果我們修改Target.apply方法接受更多參數:
1 2 3 4 5 6 |
class Target(object): def apply(value, are_you_sure): if are_you_sure: return value else: return None |
重新運行你的測試,然后你會(huì )發(fā)現它仍然能夠通過(guò)。這是因為它不是針對你的API創(chuàng )建的。這就是為什么你總是應該使用create_autospec方法,并且在使用@patch和@patch.object裝飾方法時(shí)使用autospec參數。
真實(shí)世界的例子: 模仿一次 Facebook API 調用
在結束之際,讓我寫(xiě)一個(gè)更加實(shí)用的真實(shí)世界的例子, 這在我們的介紹部分曾今提到過(guò): 向Facebook發(fā)送一個(gè)消息. 我們會(huì )寫(xiě)一個(gè)漂亮的封裝類(lèi),和一個(gè)產(chǎn)生回應的測試用例.
1 2 3 4 5 6 7 8 |
import facebookclass SimpleFacebook(object): def __init__(self, oauth_token): self.graph = facebook.GraphAPI(oauth_token) def post_message(self, message): """Posts a message to the Facebook wall.""" self.graph.put_object("me", "feed", message=message) |
原文轉自:http://www.diggerplus.org/archives/2704