代碼測(cè)試用例入門

在計(jì)算機(jī)編程中永丝,單元測(cè)試(英語:Unit Testing)又稱為模塊測(cè)試, 是針對(duì)程序模塊(軟件設(shè)計(jì)的最小單位)來進(jìn)行正確性檢驗(yàn)的測(cè)試工作。程序單元是應(yīng)用的最小可測(cè)試部件箭养。在過程化編程中慕嚷,一個(gè)單元就是單個(gè)程序、函數(shù)、過程等喝检;對(duì)于面向?qū)ο缶幊绦崂保钚卧褪欠椒ǎɑ悾ǔ悾┠铀怠⒊橄箢愒杼贰⒒蛘吲缮悾ㄗ宇悾┲械姆椒ā?/p>

通常來說,程序員每修改一次程序就會(huì)進(jìn)行最少一次單元測(cè)試损俭,在編寫程序的過程中前后很可能要進(jìn)行多次單元測(cè)試蛙奖,以證實(shí)程序達(dá)到軟件規(guī)格書要求的工作目標(biāo),沒有程序錯(cuò)誤杆兵;雖然單元測(cè)試不是什么必須的外永,但也不壞,這牽涉到項(xiàng)目管理的政策決定拧咳。

每個(gè)理想的測(cè)試案例獨(dú)立于其它案例;為測(cè)試時(shí)隔離模塊囚灼,經(jīng)常使用stubs骆膝、mock或fake等測(cè)試馬甲程序。單元測(cè)試通常由軟件開發(fā)人員編寫灶体,用于確保他們所寫的代碼匹配軟件需求和遵循開發(fā)目標(biāo)阅签。它的實(shí)施方式可以是非常手動(dòng)的(通過紙筆),或者是做成構(gòu)建自動(dòng)化的一部分蝎抽。

測(cè)試的通用規(guī)則:

  • 測(cè)試單元應(yīng)該集中于小部分的功能政钟,并且證明它是對(duì)的。
  • 每個(gè)測(cè)試單元應(yīng)該完全獨(dú)立樟结。
  • 通過Mock去除依賴
  • 盡量使測(cè)試單元快速運(yùn)行养交。
  • 實(shí)現(xiàn)鉤子來持續(xù)集成

我們通過一個(gè)簡(jiǎn)單的python程序及unittest作為示例來為大家介紹如何進(jìn)行測(cè)試,這里推薦大家使用python3來運(yùn)行示例瓢宦。

我們先創(chuàng)建一個(gè)將會(huì)使用的測(cè)試目錄

mkdir /tmp/TestHookTest
cd /tmp/TestHookTest

測(cè)試單元應(yīng)該集中于小部分的功能碎连,并且證明它是對(duì)的

下圖為unittest包中包含的斷言

我們現(xiàn)在來寫一個(gè)通過用戶名獲得github信息的一個(gè)函數(shù),并對(duì)這個(gè)函數(shù)進(jìn)行測(cè)試

# test.py
import unittest
import json

import requests

def fetch_github_profile(username):
    response = requests.get('https://api.github.com/users/' + username)
    return response.json()

class SaveDataTest(unittest.TestCase):

    def test_fetch_github_profile(self):
        username = 'ZhangBohan'
        data = fetch_github_profile('ZhangBohan')
        self.assertEqual(data['login'], username)

通過python3 -m unittest test運(yùn)行

每個(gè)測(cè)試單元應(yīng)該完全獨(dú)立

  • 每個(gè)都能夠單獨(dú)運(yùn)行驮履,除了調(diào)用的命令鱼辙,都需在測(cè)試套件中。要想實(shí)現(xiàn)這個(gè)規(guī)則玫镐,測(cè)試單元應(yīng)該加載最新的數(shù)據(jù)集倒戏,之后再做一些清理。

  • 如果有數(shù)據(jù)庫依賴恐似,在每次測(cè)試前創(chuàng)建測(cè)試數(shù)據(jù)庫杜跷,結(jié)束后銷毀該數(shù)據(jù)庫,測(cè)試應(yīng)該有單獨(dú)的數(shù)據(jù)庫,不要在生產(chǎn)和開發(fā)環(huán)境測(cè)試葱椭,避免數(shù)據(jù)變化引起的測(cè)試失敗

  • 通過Mock去除依賴

假設(shè)我們現(xiàn)在想把取得的用戶數(shù)據(jù)保存到本地捂寿,并測(cè)試是否正確保存

# test.py
import unittest
import json

import requests

def fetch_github_profile(username):
    response = requests.get('https://api.github.com/users/' + username)
    return response.json()

def save_data(data):
    with open('data.json', 'w') as f:
        f.write(json.dumps(data))

class SaveDataTest(unittest.TestCase):

    def test_fetch_github_profile(self):
        username = 'ZhangBohan'
        data = fetch_github_profile('ZhangBohan')
        self.assertEqual(data['login'], username)

    def test_save_data(self):
        data = fetch_github_profile('ZhangBohan')
        save_data(data)

        with open('data.json') as f:
            file_data = json.loads(f.read())
            self.assertIsNotNone(file_data)
            self.assertEqual(data['id'], file_data['id'])

在這個(gè)測(cè)試中我們的test_save_data中的data依賴fetch_github_profile中的返回?cái)?shù)據(jù),現(xiàn)實(shí)情況中會(huì)遇到更為復(fù)雜的依賴孵运,為了一個(gè)測(cè)試用例秦陋,我們可能需要構(gòu)建大量的初始化數(shù)據(jù)。我們可以通過mock來解除這個(gè)依賴治笨,讓test_save_data專注于測(cè)試保存數(shù)據(jù)部分

# test.py
import unittest
import json
from unittest.mock import MagicMock

import requests

def fetch_github_profile(username):
    response = requests.get('https://api.github.com/users/' + username)
    return response.json()

def save_data(data):
    with open('data.json', 'w') as f:
        f.write(json.dumps(data))


FAKE_PROFILE_DATA = {
  "login": "ZhangBohan",
  "id": 2317407
}

class SaveDataTest(unittest.TestCase):

    def test_fetch_github_profile(self):
        username = 'ZhangBohan'
        data = fetch_github_profile('ZhangBohan')
        self.assertEqual(data['login'], username)

    def test_save_data(self):
        fetch_github_profile = MagicMock(return_value=FAKE_PROFILE_DATA)
        data = fetch_github_profile('ZhangBohan')
        save_data(data)

        with open('data.json') as f:
            file_data = json.loads(f.read())
            self.assertIsNotNone(file_data)
            self.assertEqual(data['id'], file_data['id'])

盡量使測(cè)試單元快速運(yùn)行

如果一個(gè)單獨(dú)的測(cè)試單元需要較長(zhǎng)的時(shí)間去運(yùn)行驳概,開發(fā)進(jìn)度將會(huì)延遲,測(cè)試單元將不能如期常態(tài)性運(yùn)行旷赖。有時(shí)候顺又,因?yàn)闇y(cè)試單元需要復(fù)雜的數(shù)據(jù)結(jié)構(gòu),并且當(dāng)它運(yùn)行時(shí)每次都要加載等孵,所以其運(yùn)行時(shí)間較長(zhǎng)稚照。把運(yùn)行吃力的測(cè)試單元放在單獨(dú)的測(cè)試組件中,并且按照需要運(yùn)行其它測(cè)試單元俯萌。

實(shí)現(xiàn)hook來持續(xù)集成

通過代碼提交的本地hook或者webhook來持續(xù)集成測(cè)試你的代碼果录。

舉個(gè)git本地hook的例子(這可假設(shè)你了解git hook的工作原理)。

> git init
> vim .git/hooks/pre-commit

.git/hooks/pre-commit文件中寫入

#!/bin/sh

cd /tmp/TestHookTest && python3 -m unittest test

執(zhí)行:

> chmod +x .git/hooks/pre-commit

> git add test.py
> git commit -m "test hook"
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
[master (root-commit) b390117] test hook
 1 file changed, 9 insertions(+)
 create mode 100644 test.py

在遠(yuǎn)程代碼倉庫部署的webhook能更好的測(cè)試全部代碼咐熙。

Python指南-測(cè)試你的代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末弱恒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子棋恼,更是在濱河造成了極大的恐慌返弹,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爪飘,死亡現(xiàn)場(chǎng)離奇詭異义起,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)悦施,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門并扇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抡诞,你說我怎么就攤上這事穷蛹。” “怎么了昼汗?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵肴熏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我顷窒,道長(zhǎng)蛙吏,這世上最難降的妖魔是什么源哩? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮鸦做,結(jié)果婚禮上励烦,老公的妹妹穿的比我還像新娘。我一直安慰自己泼诱,他們只是感情好坛掠,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著治筒,像睡著了一般屉栓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上耸袜,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天友多,我揣著相機(jī)與錄音,去河邊找鬼堤框。 笑死域滥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蜈抓。 我是一名探鬼主播骗绕,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼资昧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起荆忍,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤格带,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后刹枉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叽唱,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年微宝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棺亭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蟋软,死狀恐怖镶摘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情岳守,我是刑警寧澤凄敢,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站湿痢,受9級(jí)特大地震影響涝缝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一拒逮、第九天 我趴在偏房一處隱蔽的房頂上張望罐氨。 院中可真熱鬧,春花似錦滩援、人聲如沸栅隐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽约啊。三九已至,卻和暖如春佣赖,著一層夾襖步出監(jiān)牢的瞬間恰矩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工憎蛤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留外傅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓俩檬,卻偏偏與公主長(zhǎng)得像萎胰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子棚辽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理技竟,服務(wù)發(fā)現(xiàn),斷路器屈藐,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,522評(píng)論 25 707
  • Startup 單元測(cè)試的核心價(jià)值在于兩點(diǎn): 更加精確地定義某段代碼的作用榔组,從而使代碼的耦合性更低 避免程序員寫出...
    wuwenxiang閱讀 10,083評(píng)論 1 27
  • 本文試圖總結(jié)編寫單元測(cè)試的流程,以及自己在寫單元測(cè)試時(shí)踩到的一些坑联逻。如有遺漏搓扯,純屬必然,歡迎補(bǔ)充包归。 目錄概覽: 編...
    蘇尚君閱讀 3,416評(píng)論 0 4
  • 很想為自己的青春 大哭一場(chǎng) 剛看到一篇寫我高中母校的文章咽白,看到高中的照片倔监,教室用含,那瞬間遥椿,眼淚直逼眼眶沾歪。不是太美好的...
    雪梨搖閱讀 220評(píng)論 0 0