python接口自動(dòng)化—mock模塊的基本使用


一、mock簡(jiǎn)介

mock原是python的第三方庫(kù)

python3以后mock模塊已經(jīng)整合到了unittest測(cè)試框架中,不用再單獨(dú)安裝

Mock這個(gè)詞在英語(yǔ)中有模擬的意思谚殊,因此我們可以猜測(cè)出這個(gè)庫(kù)的主要功能是模擬一些東西

準(zhǔn)確的說(shuō),Mock是Python中一個(gè)用于支持單元測(cè)試的庫(kù)浑测,它的主要功能是使用mock對(duì)象替代掉指定的Python對(duì)象档礁,以達(dá)到模擬對(duì)象的行為

既然mock已經(jīng)被整合到了unittest單元測(cè)試框架中,可想而知mock的目的就是為了讓我們更好的進(jìn)行測(cè)試

二赚抡、mock作用

1. 解決依賴(lài)問(wèn)題:當(dāng)我們測(cè)試一個(gè)接口或者功能模塊的時(shí)候爬坑,如果這個(gè)接口或者功能模塊依賴(lài)其他接口或其他模塊,那么如果所依賴(lài)的接口或功能模塊未開(kāi)發(fā)完畢涂臣,那么我們就可以

使用mock模擬被依賴(lài)接口盾计,完成目標(biāo)接口的測(cè)試

2. 單元測(cè)試:如果某個(gè)功能未開(kāi)發(fā)完成,我們又要進(jìn)行測(cè)試用例的代碼編寫(xiě)赁遗,我們也可以先模擬這個(gè)功能進(jìn)行測(cè)試

3. 模擬復(fù)雜業(yè)務(wù)的接口:實(shí)際工作中如果我們?cè)跍y(cè)試一個(gè)接口功能時(shí)署辉,如果這個(gè)接口依賴(lài)一個(gè)非常復(fù)雜的接口業(yè)務(wù),那么我們完全可以使用mock來(lái)模擬這個(gè)復(fù)雜的業(yè)務(wù)接口岩四,其實(shí)

這個(gè)和解決接口依賴(lài)是一樣的原理

4.前后端聯(lián)調(diào):如果你是一個(gè)前端頁(yè)面開(kāi)發(fā)哭尝,現(xiàn)在需要開(kāi)發(fā)一個(gè)功能:根據(jù)后臺(tái)返回的狀態(tài)展示不同的頁(yè)面,那么你就需要調(diào)用后臺(tái)的接口剖煌,但是后臺(tái)接口還未開(kāi)發(fā)完成材鹦,是不是你

就停止這部分工作呢?答案是否定的耕姊,你完全可以借助mock來(lái)模擬后臺(tái)這個(gè)接口返回你想要的數(shù)據(jù)

三桶唐、mock安裝

python 3 的mock模塊已經(jīng)被整合到了unittest框架中,所以你使用的時(shí)候只需要在文件開(kāi)頭from unittest import mock 導(dǎo)入即可

如果你使用的是python2 那么你需要執(zhí)行pip install mock安裝后再 import mock即可

四茉兰、mock實(shí)例

一個(gè)未開(kāi)發(fā)完成的功能如何測(cè)試莽红?

假如們現(xiàn)在有一個(gè)實(shí)現(xiàn)兩個(gè)數(shù)相加的功能需要編寫(xiě)測(cè)試用例,但是由于開(kāi)發(fā)進(jìn)度緩慢邦邦,只搭兩個(gè)簡(jiǎn)單的框架,并沒(méi)有內(nèi)部實(shí)現(xiàn)

"""

------------------------------------

@Time : 2020/7/21 14:09

@Auth : 梵音

@File : ClassFunc.py

@IDE? : PyCharm

@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

@QQ? : 829792258@qq.com

@GROUP: 829792258

------------------------------------

"""

import unittest

from unittest import mock

class SubClass(object):

? ? def add(self, a, b):

? ? ? ? """兩個(gè)數(shù)相加"""

? ? ? ? pass

class TestSub(unittest.TestCase):

  """測(cè)試兩個(gè)數(shù)相加用例"""

? ? def test_sub(self):

? ? ? ? sub = SubClass()? # 初始化被測(cè)函數(shù)類(lèi)實(shí)例

? ? ? ? sub.add = mock.Mock(return_value=10)? # mock add方法 返回10

? ? ? ? result = sub.add(5, 5)? # 調(diào)用被測(cè)函數(shù)

? ? ? ? self.assertEqual(result, 10)? # 斷言實(shí)際結(jié)果和預(yù)期結(jié)果

if __name__ == '__main__':

? ? unittest.main()

測(cè)試結(jié)果

----------------------------------------------------------------------

Ran 1 test in 0.000s

OK

Process finished with exit code

測(cè)試結(jié)果顯示醉蚁,測(cè)試用例執(zhí)行已經(jīng)通過(guò)

實(shí)際上mock模擬add方法的原理是 使用相同的對(duì)象方法接收mock的對(duì)象(使用sub.add接收)燃辖,那么當(dāng)mock對(duì)象被調(diào)用時(shí)(sub.add())就會(huì)返回return_value參數(shù)對(duì)應(yīng)的數(shù)據(jù)

你可以做一個(gè)實(shí)驗(yàn),把用例中的add改成別的名字也一樣可以測(cè)試通過(guò)

我們用例編寫(xiě)完了网棍,而且開(kāi)發(fā)既然也把功能開(kāi)發(fā)完了黔龟,既然真實(shí)的功能已經(jīng)可以測(cè)試了,那么我們?cè)趺丛谏厦嬗美幕A(chǔ)上直接測(cè)試真實(shí)功能呢?

完整的功能如何測(cè)試氏身?

我們把用例的代碼稍做修改

"""

------------------------------------

@Time : 2020/7/21 14:09

@Auth : 梵音

@File : ClassFunc.py

@IDE? : PyCharm

@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

@QQ? : 829792258@qq.com

@GROUP: 829792258

------------------------------------

"""

import unittest

from unittest import mock

class SubClass(object):

? ? def add(self, a, b):

? ? ? ? """兩個(gè)數(shù)相加"""

? ? ? ? return a + b

class TestSub(unittest.TestCase):

? ? """測(cè)試兩個(gè)數(shù)相加"""

? ? def test_sub(self):

? ? ? ? sub = SubClass()? # 初始化被測(cè)函數(shù)類(lèi)實(shí)例

? ? ? ? sub.add = mock.Mock(return_value=10, side_effect=sub.add)? # 傳遞side_effect關(guān)鍵字參數(shù), 會(huì)覆蓋return_value參數(shù)值, 使用真實(shí)的add方法測(cè)試

? ? ? ? result = sub.add(5, 11)? # 真正的調(diào)用被測(cè)函數(shù)

? ? ? ? self.assertEqual(result, 16)? # 斷言實(shí)際結(jié)果和預(yù)期結(jié)果

if __name__ == '__main__':

? ? unittest.main()

side_effect參數(shù)

代碼中我們給Mock方法添加了另一個(gè)關(guān)鍵字參數(shù)side_effect = sub.add巍棱, 這個(gè)參數(shù)和return_value 正好相反,當(dāng)傳遞這個(gè)參數(shù)的時(shí)候return_value 參數(shù)就會(huì)失效

而side_effect生效蛋欣,這里我給的參數(shù)值是sub.add 相當(dāng)于add方法的地址航徙,那么當(dāng)調(diào)用add方法時(shí)就會(huì)真實(shí)的使用add方法,也就達(dá)到了我們測(cè)試實(shí)際的add 方法陷虎。

你也可以理解為當(dāng)傳遞了side_effect參數(shù)且值為被測(cè)方法地址時(shí)到踏,mock就不會(huì)起作用

side_effect接收的是一個(gè)可迭代序列,當(dāng)傳遞多個(gè)值時(shí)尚猿,那么每次調(diào)用mock時(shí)會(huì)返回不同的值

mock_obj = mock.Mock(side_effect= [1,2,3])

print(mock_obj())

print(mock_obj())

print(mock_obj())

print(mock_obj())

輸出

Traceback (most recent call last):

1

? File "D:/MyThreading/mymock.py", line 37, in <module>

2

? ? print(mock_obj())

3

? File "C:\Python36\lib\unittest\mock.py", line 939, in __call__

? ? return _mock_self._mock_call(*args, **kwargs)

? File "C:\Python36\lib\unittest\mock.py", line 998, in _mock_call

? ? result = next(effect)

StopIteration

Process finished with exit code 1

當(dāng)所有值被取完后就會(huì)報(bào)錯(cuò)(這個(gè)地方有點(diǎn)類(lèi)似生成器的原理)

存在依賴(lài)關(guān)系的功能如何測(cè)試窝稿?

假設(shè)有這樣一個(gè)場(chǎng)景:我們要測(cè)試一個(gè)支付接口但是這個(gè)支付接口又依賴(lài)一個(gè)第三方支付接口,那么第三方支付接口我們暫時(shí)沒(méi)有權(quán)限使用凿掂,那么我們?cè)撊绾螠y(cè)試我們自己這個(gè)接口呢伴榔?

看下面的實(shí)例

假設(shè)第三方接口和我們自己的支付接口如下

"""

------------------------------------

@Time :2020/7/21 15:09

@Auth : 梵音

@File : PayMent.py

@IDE? : PyCharm

@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

@QQ? : 829792258@qq.com

@GROUP: 829792258

------------------------------------

"""

import requests

class PayApi(object):

? ? @staticmethod

? ? def auth(card, amount):

? ? ? ? """

? ? ? ? 第三方支付接口

? ? ? ? :param card: 卡號(hào)

? ? ? ? :param amount: 支付金額

? ? ? ? :return:

? ? ? ? """

? ? ? ? pay_url = "http://www.zhifubao.com"? # 第三方支付接口地址

? ? ? ? data = {"card": card, "amount": amount}

? ? ? ? response = requests.post(pay_url, data=data)? # 請(qǐng)求第三方支付接口

? ? ? ? return response? # 返回狀態(tài)碼

? ? def pay(self, user_id, card, amount):

? ? ? ? """

? ? ? ? 我們自己的支付接口

? ? ? ? :param user_id: 用戶(hù)id

? ? ? ? :param card: 卡號(hào)

? ? ? ? :param amount: 支付金額

? ? ? ? :return:

? ? ? ? """

     # 調(diào)用第三方支付接口

? ? ? ? response = self.auth(card, amount)

? ? ? ? try:

? ? ? ? ? ? if response['status_code'] == '200':

? ? ? ? ? ? ? ? print('用戶(hù){}支付金額{}成功'.format(user_id, amount))

? ? ? ? ? ? ? ? return '支付成功'

? ? ? ? ? ? elif response['status_code'] == '500':

? ? ? ? ? ? ? ? print('用戶(hù){}支付失敗, 金額不變'.format(user_id))

? ? ? ? ? ? ? ? return '支付失敗'

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? return '未知錯(cuò)誤'

? ? ? ? except Exception:

? ? ? ? ? ? return "Error, 服務(wù)器異常!"

if __name__ == '__main__':

? ? pass

很明顯第三方支付接口是無(wú)法訪問(wèn)的,因?yàn)榻涌诘牡刂肥俏褼IY的庄萎,為了模擬實(shí)際中我們無(wú)法使用的第三方支付接口

編寫(xiě)測(cè)試用例

"""

------------------------------------

@Time : 2020/7/21 15:22

@Auth : 梵音

@File : testpay.py

@IDE? : PyCharm

@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

@QQ? : 829792258@qq.com

@GROUP: 829792258

------------------------------------

"""

import unittest

from unittest import mock

from payment.PayMent import PayApi

class TestPayApi(unittest.TestCase):

? ? def test_success(self):

? ? ? ? pay = PayApi()

? ? ? ? pay.auth = mock.Mock(return_value={'status_code':'200'})

? ? ? ? status = pay.pay('1000', '12345', '10000')

? ? ? ? self.assertEqual(status, '支付成功')

? ? def test_fail(self):

? ? ? ? pay = PayApi()

? ? ? ? pay.auth = mock.Mock(return_value={'status_code':'500'})

? ? ? ? status = pay.pay('1000', '12345', '10000')

? ? ? ? self.assertEqual(status, '支付失敗')

? ? def test_error(self):

? ? ? ? pay = PayApi()

? ? ? ? pay.auth = mock.Mock(return_value={'status_code':'300'})

? ? ? ? status = pay.pay('1000', '12345', '10000')

? ? ? ? self.assertEqual(status, '未知錯(cuò)誤')

? ? def test_exception(self):

? ? ? ? pay = PayApi()

? ? ? ? pay.auth = mock.Mock(return_value='200')

? ? ? ? status = pay.pay('1000', '12345', '10000')

? ? ? ? self.assertEqual(status, 'Error, 服務(wù)器異常!')

if __name__ == '__main__':

? ? unittest.main()

測(cè)試輸出結(jié)果

....用戶(hù)1000支付失敗, 金額不變

用戶(hù)1000支付金額10000成功

----------------------------------------------------------------------

Ran 4 tests in 0.001s

OK

Process finished with exit code 0

從執(zhí)行結(jié)果可以看出踪少,即使第三方支付接口無(wú)法使用,但是我們自己的支付接口仍然測(cè)試通過(guò)了

也許有人會(huì)問(wèn)惨恭,第三方支付都不能用秉馏,我們的測(cè)試結(jié)果是否是有效的呢?

通常我們?cè)跍y(cè)試一個(gè)模塊的時(shí)候脱羡,我們是可以認(rèn)為其他模塊的功能是正常的萝究,只針對(duì)目標(biāo)模塊進(jìn)行測(cè)試是沒(méi)有任何問(wèn)題的,所以說(shuō)測(cè)試結(jié)果也是正確的

其實(shí)上述代碼還可以使用另一種方式來(lái)寫(xiě)

mock對(duì)象的方法

"""

------------------------------------

@Time : 2020/7/21 15:22

@Auth :梵音

@File : testpay.py

@IDE? : PyCharm

@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

@QQ? : 829792258@qq.com

@GROUP:829792258

------------------------------------

"""

import unittest

from unittest import mock

from unittest.mock import patch

from payment.PayMent import PayApi

class TestPayApi(unittest.TestCase):

? ? def setUp(self):

? ? ? ? self.pay = PayApi()

? ? @patch.object(PayApi, 'auth')

? ? def test_success(self, mock_auth):

? ? ? ? mock_auth.return_value = {'status_code':'200'}

? ? ? ? status = self.pay.pay('1000', '12345', '10000')

? ? ? ? self.assertEqual(status, '支付成功')

? ? @patch.object(PayApi, 'auth')

? ? def test_fail(self, mock_auth):

? ? ? ? mock_auth.return_value={'status_code':'500'}

? ? ? ? status = self.pay.pay('1000', '12345', '10000')

? ? ? ? self.assertEqual(status, '支付失敗')

? ? @patch.object(PayApi, 'auth')

? ? def test_error(self, mock_auth):

? ? ? ? mock_auth.return_value={'status_code':'300'}

? ? ? ? status = self.pay.pay('1000', '12345', '10000')

? ? ? ? self.assertEqual(status, '未知錯(cuò)誤')

? ? @patch.object(PayApi, 'auth')

? ? def test_exception(self, mock_auth):

? ? ? ? mock_auth.return_value='200'

? ? ? ? status = self.pay.pay('1000', '12345', '10000')

? ? ? ? self.assertEqual(status, 'Error, 服務(wù)器異常!')

if __name__ == '__main__':

? ? unittest.main()

還有mock一個(gè)普通函數(shù)锉罐,mock多個(gè)方法等帆竹,這里先不贅述,寫(xiě)法和上面實(shí)例差不多創(chuàng)建了一個(gè)測(cè)試交流群脓规,如果對(duì)軟件測(cè)試栽连、接口測(cè)試、自動(dòng)化測(cè)試侨舆、面試經(jīng)驗(yàn)交流感興趣可以加測(cè)試交流群:829792258秒紧,還會(huì)有同行一起技術(shù)交流

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市挨下,隨后出現(xiàn)的幾起案子熔恢,更是在濱河造成了極大的恐慌,老刑警劉巖臭笆,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叙淌,死亡現(xiàn)場(chǎng)離奇詭異秤掌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)鹰霍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)闻鉴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人茂洒,你說(shuō)我怎么就攤上這事孟岛。” “怎么了获黔?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵蚀苛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我玷氏,道長(zhǎng)堵未,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任盏触,我火速辦了婚禮渗蟹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赞辩。我一直安慰自己雌芽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布辨嗽。 她就那樣靜靜地躺著世落,像睡著了一般。 火紅的嫁衣襯著肌膚如雪糟需。 梳的紋絲不亂的頭發(fā)上屉佳,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音洲押,去河邊找鬼武花。 笑死,一個(gè)胖子當(dāng)著我的面吹牛杈帐,可吹牛的內(nèi)容都是我干的体箕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼挑童,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼累铅!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起站叼,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤娃兽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后大年,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體换薄,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年翔试,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了轻要。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡垦缅,死狀恐怖冲泥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情壁涎,我是刑警寧澤凡恍,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站怔球,受9級(jí)特大地震影響嚼酝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜竟坛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一闽巩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧担汤,春花似錦涎跨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至率碾,卻和暖如春叔营,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背播掷。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工审编, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人歧匈。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓垒酬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親件炉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子勘究,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348