Python單元測試和Mock測試

單元測試

測試可以保證你的代碼在一系列給定條件下正常工作

測試允許人們確保對代碼的改動不會破壞現(xiàn)有的功能

測試迫使人們在不尋常條件的情況下思考代碼,這可能會揭示出邏輯錯誤

良好的測試要求模塊化和敬,解耦代碼凹炸,這是一個良好的系統(tǒng)設(shè)計的標(biāo)志

示例

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import os, sys

import time, datetime

import unittest

from unittest import TestCase

class TestSequenece(TestCase):

????def setUp(self):

????????self.lst = range(10)

????????print "setUp starting ..."

????def test_eq(self):

????????print "test_eq starting..."

????????self.assertEqual(self.lst, range(10))

????def test_in(self):

????????print "test_in starting..."

????????self.assertIn(1, self.lst)

????????self.assertNotIn(10, self.lst)

????def test_instance(self):

????????print "test_instance starting..."

????????self.assertIsInstance(self.lst, list)

????def tearDown(self):

????????print "tearDown starting..."

if __name__ == '__main__':

????unittest.main()

然后我們看一下執(zhí)行結(jié)果再分析:

setUp starting ...

test_eq starting...

tearDown starting...

.setUp starting ...

test_in starting...

tearDown starting...

.setUp starting ...

test_instance starting...

tearDown starting...

.

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

Ran 3 tests in 0.000s

OK

共運行三個測試, 每次測試成功通過都會輸出一個.號

TestCase直譯就是測試用例, 一個測試用例可以包含多個測試

test_xxxx就是測試項, 根據(jù)實際的功能代碼邏輯來編寫對應(yīng)的測試項, 運行時會自動查找所有以test開發(fā)的成員函數(shù)

assertXXXX斷言語句, 用來判斷測試結(jié)果是否符合測試預(yù)期結(jié)果.

setUp是執(zhí)行每個測試項前的準(zhǔn)備工作, 比如:可以做一些初始化工作

tearDown是執(zhí)行在每個測試項后的收尾工作,銷毀測試過程中產(chǎn)生的垃圾, 恢復(fù)現(xiàn)場等

Mock測試

Mock測試是什么鬼? 我們常常遇到這樣一種場景, 我們測試一些函數(shù), 而這些函數(shù)內(nèi)部調(diào)用另外帶有副作用的操作, 這可能導(dǎo)致我們在測試過程中對數(shù)據(jù)造成未知的副作用, 而這并不是我們希望在測試中看到的.

Mock測試可以替換到指定的Python對象或者方法, 并自定義指定對象或者方法的返回值, 從來模擬對象或者方法, 消除副作用.

Mock在Python3.3時加入到標(biāo)準(zhǔn)庫中, 2.X版本可以通過pip安裝

$ pip install mock

首先任意寫一個函數(shù)

# -*- coding: utf-8 -*-

#!/usr/bin/env python

import os, sys, time

def foo():

????lst = [1]

????lst = give_me_five(lst)

????return lst

def give_me_five(lst):

????return lst * 5

我們希望通過單元測試來測試這個函數(shù)的邏輯正確性.

# -*- coding: utf-8 -*-

#!/usr/bin/env python

import os, sys, time

# ?sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

import unittest

from unittest import TestCase

import mock

import module

class Foo(object):

????pass

class TestMock(TestCase):

????# 1

????def test_method(self):

????????obj = Foo()

????????obj.method = mock.MagicMock(return_value=3)

????????print obj.method

????????self.assertEqual(obj.method(4), 3)

????# 2

????@mock.patch('module.foo')

????def test_decorator(self, foo):

????????# ?res = module.foo()

????????foo.return_value = [1, 2, 3]

????????self.assertEqual(foo(), [1, 2, 3])

????# 3

????def test_with(self):

????????with mock.patch('module.give_me_five') as give_me_five:

????????????give_me_five.return_value = "I'm Mock"

????????????self.assertEqual(module.foo(), "I'm Mock")

????# 4

????def test_module(self):

????????module.give_me_five = mock.Mock(return_value=[1] * 5)

????????module.give_me_five([1]) ?# 此時已經(jīng)變成了一個Mock對象, 并嘗試調(diào)用

????????module.give_me_five.assert_called_with([1]) ?# 對mock的參數(shù)進(jìn)行斷言

????????self.assertEqual(module.foo(), [1] * 5)

if __name__ == '__main__':

????unittest.main()

我們首先集成TestCase創(chuàng)建了一個單元測試

# 1位置, 我們通過mock提供的函數(shù)給obj的method方法設(shè)置返回值(可以看到類中并不包含method方法). 最后通過斷言來判斷返回值等于我們通過MagicMock設(shè)置的返回值

# 2位置, 我們通過mock提供的裝飾器,?patch()可以作為函數(shù)做裝飾, 類裝飾器, 上下文管理器?將module中的foo函數(shù)給mock掉,?并且并mock的函數(shù)生成的Mock對象作為類成員函數(shù)參數(shù)傳入, 指定了foo函數(shù)的返回值, 并通過了斷言測試

# 3位置, 將patch()作為一個上下文管理, 關(guān)于上下文管理器可以看我另一篇文章Python奇技淫巧, 用法和作為裝飾器基本類似

# 4位置, 我們調(diào)用module.foo函數(shù), 而我們并不關(guān)系foo()調(diào)用了那些函數(shù), 我只關(guān)心在成功調(diào)用module.give_me_five后, foo函數(shù)的邏輯正確性. 所以此次我們通過Mock函數(shù)給module.give_me_five指定我們希望的返回值. 這樣就能獨立的測試module.foo的邏輯

mock的主要思想: 通過mock對象對某些函數(shù)進(jìn)行替換, 對在測試上下文中, 這些被mock的函數(shù)被重定向到指定的mock對象

mock還有一些更高級的應(yīng)用

MagicMock是Mock的子類, 并且包含一些如__str__一樣的黑魔法函數(shù), 使用MagicMock甚至可以mock掉黑魔法函數(shù)

通過patch.object可以mock掉類中指定的成員函數(shù)

通過patch.dict可以將對象mock為字典

通過patch中的start和stop方法可以控制mock的生效范圍, 更加靈活的運行mock測試

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市昼弟,隨后出現(xiàn)的幾起案子啤它,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件变骡,死亡現(xiàn)場離奇詭異离赫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)塌碌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門渊胸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人台妆,你說我怎么就攤上這事翎猛。” “怎么了接剩?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵切厘,是天一觀的道長。 經(jīng)常有香客問我搂漠,道長迂卢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任桐汤,我火速辦了婚禮而克,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怔毛。我一直安慰自己员萍,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布拣度。 她就那樣靜靜地躺著碎绎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抗果。 梳的紋絲不亂的頭發(fā)上筋帖,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天,我揣著相機(jī)與錄音冤馏,去河邊找鬼日麸。 笑死,一個胖子當(dāng)著我的面吹牛逮光,可吹牛的內(nèi)容都是我干的代箭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼涕刚,長吁一口氣:“原來是場噩夢啊……” “哼嗡综!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起杜漠,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤极景,失蹤者是張志新(化名)和其女友劉穎察净,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盼樟,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡塞绿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了恤批。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片异吻。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖喜庞,靈堂內(nèi)的尸體忽然破棺而出诀浪,到底是詐尸還是另有隱情,我是刑警寧澤延都,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布雷猪,位于F島的核電站,受9級特大地震影響晰房,放射性物質(zhì)發(fā)生泄漏求摇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一殊者、第九天 我趴在偏房一處隱蔽的房頂上張望与境。 院中可真熱鬧,春花似錦猖吴、人聲如沸摔刁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽共屈。三九已至,卻和暖如春党窜,著一層夾襖步出監(jiān)牢的瞬間拗引,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工幌衣, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留矾削,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓泼掠,卻偏偏與公主長得像怔软,于是被迫代替她去往敵國和親垦细。 傳聞我的和親對象是個殘疾皇子择镇,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容