摘要:準(zhǔn)確的說,是中一個(gè)用于支持單元測試的庫,它的主要功能是使用對象替代掉指定的對象,以達(dá)到模擬對象的行為。下面我們使用對象在單元測試中分別測試訪問正常和訪問不正常的情況。
Mock是什么
Mock這個(gè)詞在英語中有模擬的這個(gè)意思,因此我們可以猜測出這個(gè)庫的主要功能是模擬一些東西。準(zhǔn)確的說,Mock是Python中一個(gè)用于支持單元測試的庫,它的主要功能是使用mock對象替代掉指定的Python對象,以達(dá)到模擬對象的行為。簡單的說,mock庫用于如下的場景:
假設(shè)你開發(fā)的項(xiàng)目叫a,里面包含了一個(gè)模塊b,模塊b中的一個(gè)函數(shù)c(也就是a.b.c)在工作的時(shí)候需要調(diào)用發(fā)送請求給特定的服務(wù)器來得到一個(gè)JSON返回值,然后根據(jù)這個(gè)返回值來做處理。如果要為a.b.c函數(shù)寫一個(gè)單元測試,該如何做?
一個(gè)簡單的辦法是搭建一個(gè)測試的服務(wù)器,在單元測試的時(shí)候,讓a.b.c函數(shù)和這個(gè)測試服務(wù)器交互。但是這種做法有兩個(gè)問題:
測試服務(wù)器可能很不好搭建,或者搭建效率很低。
你搭建的測試服務(wù)器可能無法返回所有可能的值,或者需要大量的工作才能達(dá)到這個(gè)目的。
那么如何在沒有測試服務(wù)器的情況下進(jìn)行上面這種情況的單元測試呢?Mock模塊就是答案。上面已經(jīng)說過了,mock模塊可以替換Python對象。我們假設(shè)a.b.c的代碼如下:
import requests def c(url): resp = requests.get(url) # further process with resp
如果利用mock模塊,那么就可以達(dá)到這樣的效果:使用一個(gè)mock對象替換掉上面的requests.get函數(shù),然后執(zhí)行函數(shù)c時(shí),c調(diào)用requests.get的返回值就能夠由我們的mock對象來決定,而不需要服務(wù)器的參與。簡單的說,就是我們用一個(gè)mock對象替換掉c函數(shù)和服務(wù)器交互的過程。你一定很好奇這個(gè)功能是如何實(shí)現(xiàn)的,這個(gè)是mock模塊內(nèi)部的實(shí)現(xiàn)機(jī)制,不在本文的討論范圍。本文主要討論如何用mock模塊來解決上面提到的這種單元測試場景。
Mock的安裝和導(dǎo)入在Python 3.3以前的版本中,需要另外安裝mock模塊,可以使用pip命令來安裝:
$ sudo pip install mock
然后在代碼中就可以直接import進(jìn)來:
import mock
從Python 3.3開始,mock模塊已經(jīng)被合并到標(biāo)準(zhǔn)庫中,被命名為unittest.mock,可以直接import進(jìn)來使用:
from unittest import mockMock對象 基本用法
Mock對象是mock模塊中最重要的概念。Mock對象就是mock模塊中的一個(gè)類的實(shí)例,這個(gè)類的實(shí)例可以用來替換其他的Python對象,來達(dá)到模擬的效果。Mock類的定義如下:
class Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, **kwargs)
這里給出這個(gè)定義只是要說明下Mock對象其實(shí)就是個(gè)Python類而已,當(dāng)然,它內(nèi)部的實(shí)現(xiàn)是很巧妙的,有興趣的可以去看mock模塊的代碼。
Mock對象的一般用法是這樣的:
找到你要替換的對象,這個(gè)對象可以是一個(gè)類,或者是一個(gè)函數(shù),或者是一個(gè)類實(shí)例。
然后實(shí)例化Mock類得到一個(gè)mock對象,并且設(shè)置這個(gè)mock對象的行為,比如被調(diào)用的時(shí)候返回什么值,被訪問成員的時(shí)候返回什么值等。
使用這個(gè)mock對象替換掉我們想替換的對象,也就是步驟1中確定的對象。
之后就可以開始寫測試代碼,這個(gè)時(shí)候我們可以保證我們替換掉的對象在測試用例執(zhí)行的過程中行為和我們預(yù)設(shè)的一樣。
舉個(gè)例子來說:我們有一個(gè)簡單的客戶端實(shí)現(xiàn),用來訪問一個(gè)URL,當(dāng)訪問正常時(shí),需要返回狀態(tài)碼200,不正常時(shí),需要返回狀態(tài)碼404。首先,我們的客戶端代碼實(shí)現(xiàn)如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- import requests def send_request(url): r = requests.get(url) return r.status_code def visit_ustack(): return send_request("http://www.ustack.com")
外部模塊調(diào)用visit_ustack()來訪問UnitedStack的官網(wǎng)。下面我們使用mock對象在單元測試中分別測試訪問正常和訪問不正常的情況。
#!/usr/bin/env python # -*- coding: utf-8 -*- import unittest import mock import client class TestClient(unittest.TestCase): def test_success_request(self): success_send = mock.Mock(return_value="200") client.send_request = success_send self.assertEqual(client.visit_ustack(), "200") def test_fail_request(self): fail_send = mock.Mock(return_value="404") client.send_request = fail_send self.assertEqual(client.visit_ustack(), "404")
找到要替換的對象:我們需要測試的是visit_ustack這個(gè)函數(shù),那么我們需要替換掉send_request這個(gè)函數(shù)。
實(shí)例化Mock類得到一個(gè)mock對象,并且設(shè)置這個(gè)mock對象的行為。在成功測試中,我們設(shè)置mock對象的返回值為字符串“200”,在失敗測試中,我們設(shè)置mock對象的返回值為字符串"404"。
使用這個(gè)mock對象替換掉我們想替換的對象。我們替換掉了client.send_request
寫測試代碼。我們調(diào)用client.visit_ustack(),并且期望它的返回值和我們預(yù)設(shè)的一樣。
上面這個(gè)就是使用mock對象的基本步驟了。在上面的例子中我們替換了自己寫的模塊的對象,其實(shí)也可以替換標(biāo)準(zhǔn)庫和第三方模塊的對象,方法是一樣的:先import進(jìn)來,然后替換掉指定的對象就可以了。
稍微高級點(diǎn)的用法 class Mock的參數(shù)上面講的是mock對象最基本的用法。下面來看看mock對象的稍微高級點(diǎn)的用法(并不是很高級啊,最完整最高級的直接去看mock的文檔即可,后面給出)。
先來看看Mock這個(gè)類的參數(shù),在上面看到的類定義中,我們知道它有好幾個(gè)參數(shù),這里介紹最主要的幾個(gè):
name: 這個(gè)是用來命名一個(gè)mock對象,只是起到標(biāo)識作用,當(dāng)你print一個(gè)mock對象的時(shí)候,可以看到它的name。
return_value: 這個(gè)我們剛才使用過了,這個(gè)字段可以指定一個(gè)值(或者對象),當(dāng)mock對象被調(diào)用時(shí),如果side_effect函數(shù)返回的是DEFAULT,則對mock對象的調(diào)用會返回return_value指定的值。
side_effect: 這個(gè)參數(shù)指向一個(gè)可調(diào)用對象,一般就是函數(shù)。當(dāng)mock對象被調(diào)用時(shí),如果該函數(shù)返回值不是DEFAULT時(shí),那么以該函數(shù)的返回值作為mock對象調(diào)用的返回值。
其他的參數(shù)請參考官方文檔。
mock對象的自動創(chuàng)建當(dāng)訪問一個(gè)mock對象中不存在的屬性時(shí),mock會自動建立一個(gè)子mock對象,并且把正在訪問的屬性指向它,這個(gè)功能對于實(shí)現(xiàn)多級屬性的mock很方便。
client = mock.Mock() client.v2_client.get.return_value = "200"
這個(gè)時(shí)候,你就得到了一個(gè)mock過的client實(shí)例,調(diào)用該實(shí)例的v2_client.get()方法會得到的返回值是"200"。
從上面的例子中還可以看到,指定mock對象的return_value還可以使用屬性賦值的方法。
對方法調(diào)用進(jìn)行檢查mock對象有一些方法可以用來檢查該對象是否被調(diào)用過、被調(diào)用時(shí)的參數(shù)如何、被調(diào)用了幾次等。實(shí)現(xiàn)這些功能可以調(diào)用mock對象的方法,具體的可以查看mock的文檔。這里我們舉個(gè)例子。
還是使用上面的代碼,這次我們要檢查visit_ustack()函數(shù)調(diào)用send_request()函數(shù)時(shí),傳遞的參數(shù)類型是否正確。我們可以像下面這樣使用mock對象。
class TestClient(unittest.TestCase): def test_call_send_request_with_right_arguments(self): client.send_request = mock.Mock() client.visit_ustack() self.assertEqual(client.send_request.called, True) call_args = client.send_request.call_args self.assertIsInstance(call_args[0][0], str)
Mock對象的called屬性表示該mock對象是否被調(diào)用過。
Mock對象的call_args表示該mock對象被調(diào)用的tuple,tuple的每個(gè)成員都是一個(gè)mock.call對象。mock.call這個(gè)對象代表了一次對mock對象的調(diào)用,其內(nèi)容是一個(gè)tuple,含有兩個(gè)元素,第一個(gè)元素是調(diào)用mock對象時(shí)的位置參數(shù)(*args),第二個(gè)元素是調(diào)用mock對象時(shí)的關(guān)鍵字參數(shù)(**kwargs)。
現(xiàn)在來分析下上面的用例,我們要檢查的項(xiàng)目有兩個(gè):
visit_ustack()調(diào)用了send_request()
調(diào)用的參數(shù)是一個(gè)字符串
patch和patch.object在了解了mock對象之后,我們來看兩個(gè)方便測試的函數(shù):patch和patch.object。這兩個(gè)函數(shù)都會返回一個(gè)mock內(nèi)部的類實(shí)例,這個(gè)類是class _patch。返回的這個(gè)類實(shí)例既可以作為函數(shù)的裝飾器,也可以作為類的裝飾器,也可以作為上下文管理器。使用patch或者patch.object的目的是為了控制mock的范圍,意思就是在一個(gè)函數(shù)范圍內(nèi),或者一個(gè)類的范圍內(nèi),或者with語句的范圍內(nèi)mock掉一個(gè)對象。我們看個(gè)代碼例子即可:
class TestClient(unittest.TestCase): def test_success_request(self): status_code = "200" success_send = mock.Mock(return_value=status_code) with mock.patch("client.send_request", success_send): from client import visit_ustack self.assertEqual(visit_ustack(), status_code) def test_fail_request(self): status_code = "404" fail_send = mock.Mock(return_value=status_code) with mock.patch("client.send_request", fail_send): from client import visit_ustack self.assertEqual(visit_ustack(), status_code)
這個(gè)測試類和我們剛才寫的第一個(gè)測試類一樣,包含兩個(gè)測試,只不過這次不是顯示創(chuàng)建一個(gè)mock對象并且進(jìn)行替換,而是使用了patch函數(shù)(作為上下文管理器使用)。
patch.object和patch的效果是一樣的,只不過用法有點(diǎn)不同。舉例來說,同樣是上面這個(gè)例子,換成patch.object的話是這樣的:
def test_fail_request(self): status_code = "404" fail_send = mock.Mock(return_value=status_code) with mock.patch.object(client, "send_request", fail_send): from client import visit_ustack self.assertEqual(visit_ustack(), status_code)
就是替換掉一個(gè)對象的指定名稱的屬性,用法和setattr類似。
如何學(xué)習(xí)使用mock?你肯定很奇怪,本文不就是教人使用mock的么?其實(shí)不是的,我發(fā)現(xiàn)自己在學(xué)習(xí)mock的過程中遇到的主要困難是不清楚mock能做什么,而不是mock對象到底有哪些函數(shù)。因此寫這篇文章的主要目的是為了說明mock能做什么。
當(dāng)你知道了mock能做什么之后,要如何學(xué)習(xí)并掌握mock呢?最好的方式就是查看閱讀官方文檔,并在自己的單元測試中使用。
最后,學(xué)習(xí)mock技能你應(yīng)該要能夠感受到一種控制的快感,就是你能享受控制外部服務(wù)的快樂。當(dāng)你感受到這種快感的時(shí)候,你的mock應(yīng)該就達(dá)到熟練使用的水平了。
官方文檔 Python 2.7mock還未加入標(biāo)準(zhǔn)庫。
http://www.voidspace.org.uk/python/mock/index.html
Python 3.4mock已經(jīng)加入了標(biāo)準(zhǔn)庫。
https://docs.python.org/3.4/library/unittest.mock-examples.html
https://docs.python.org/3.4/library/unittest.mock.html
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/37557.html
摘要:本文將進(jìn)入單元測試的部分,這也是基礎(chǔ)知識中最后一個(gè)大塊。本文將重點(diǎn)講述和中的單元測試的生態(tài)環(huán)境。另外,在中指定要運(yùn)行的單元測試用例的完整語法是。中使用模塊管理單元測試用例。每個(gè)項(xiàng)目的單元測試代碼結(jié)構(gòu)可 本文將進(jìn)入單元測試的部分,這也是基礎(chǔ)知識中最后一個(gè)大塊。本文將重點(diǎn)講述Python和OpenStack中的單元測試的生態(tài)環(huán)境。 單元測試的重要性 github上有個(gè)人畫了一些不同語言的學(xué)...
摘要:最新最全的開源項(xiàng)目合集掘金是由整理并維護(hù)的安卓相關(guān)開源項(xiàng)目庫集合。準(zhǔn)備的插件開發(fā)必開發(fā)者福利史上最全開發(fā)和安全系列工具掘金取證工具一個(gè)工具箱,用于分析手機(jī)元數(shù)據(jù)。 最新最全的 Android 開源項(xiàng)目合集 - Android - 掘金awesome-github-android-ui 是由OpenDigg整理并維護(hù)的安卓UI相關(guān)開源項(xiàng)目庫集合。我們會定期同步OpenDigg上的項(xiàng)目到這...
摘要:三使用介紹通過代碼創(chuàng)建通過注解四常用方法驗(yàn)證方法沒有被調(diào)用驗(yàn)證方法被調(diào)用了次方法至少被調(diào)用次方法最多被調(diào)用次備注假如你無法給你程序?qū)憜卧獪y試,那么意味著你的程序結(jié)構(gòu)有問題,需要調(diào)整或重構(gòu)。 Java單元測試入門 什么是單元測試 定義:單元測試是對軟件或程序的基本(最小)組成單元的測試對象:方法、類特點(diǎn):showImg(https://segmentfault.com/img/bVbcR...
摘要:接下來我們將介紹如何對對象的方法進(jìn)行模擬測試。選項(xiàng)創(chuàng)建模擬測試接口我們可以在的構(gòu)造函數(shù)中提供一個(gè)模擬測試實(shí)例,而不是模擬創(chuàng)建具體的模擬測試方法。 如何不靠耐心測試 通常,我們編寫的軟件會直接與那些我們稱之為骯臟的服務(wù)交互。通俗地說,服務(wù)對我們的應(yīng)用來說是至關(guān)重要的,它們之間的交互是我們設(shè)計(jì)好的,但這會帶來我們不希望的副作用——就是那些在我們自己測試的時(shí)候不希望的功能。 比如,可能我們...
摘要:也就是說,如果不需要,兩者使用起來并沒有什么分別。來看個(gè)例子,先定義個(gè)類,里面只有一個(gè)成員方法,返回倍的數(shù)值使用類來掉這個(gè)成員方法使用類來兩者沒有任何區(qū)別,都成功了了成員方法。再看下兩者的區(qū)別因?yàn)槭褂妙悤r(shí),默認(rèn)不會創(chuàng)建這個(gè)的,所以報(bào)錯(cuò)。 Python的unittest.mock模塊中提供了兩個(gè)主要的mock類,分別是Mock和MagicMock. 先看一下官方文檔的定義: MagicM...
閱讀 1341·2023-04-25 23:47
閱讀 919·2021-11-23 09:51
閱讀 4457·2021-09-26 10:17
閱讀 3713·2021-09-10 11:19
閱讀 3261·2021-09-06 15:10
閱讀 3551·2019-08-30 12:49
閱讀 2425·2019-08-29 13:20
閱讀 1736·2019-08-28 18:14