一、為什么要引入單元測試
在開發(fā)過程中我們會(huì)遇到這樣一些問題:
- 面對需要重構(gòu)龐大的模塊代碼時(shí)無從下手
- 修改了一處地方卻在另一處地方引發(fā)了新的bug
- 擴(kuò)展新功能的同時(shí)導(dǎo)致舊代碼出現(xiàn)bug
- 在測試人員難以覆蓋到的基礎(chǔ)功能接口出現(xiàn)了bug
- 出現(xiàn)了一種難以重現(xiàn)的特殊邊界條件觸發(fā)的bug
另外我們也許還會(huì)遇到一些這樣的模塊:
- A模塊依賴于B模塊的結(jié)果,但是B模塊尚未開發(fā)完成
- 模塊狀態(tài)過于復(fù)雜,手工測試需要耗費(fèi)大量時(shí)間
- 模塊業(yè)務(wù)與時(shí)間節(jié)點(diǎn)相關(guān),手工測試難以覆蓋
這個(gè)時(shí)候也許能夠利用經(jīng)驗(yàn)和豐富的debug技巧來解決這些問題,但是很多時(shí)候我們的處理并不完美,因?yàn)槲覀內(nèi)鄙倭艘粋€(gè)規(guī)范,在編碼過程中難以顧及其他模塊的影響,這個(gè)時(shí)候,我們就需要引入單元測試。

二、單元測試的價(jià)值
可維護(hù)性增強(qiáng)
當(dāng)在對代碼進(jìn)行修改時(shí),利用單元測試就能夠清晰的知道是否破壞了老的業(yè)務(wù)邏輯,這樣大大減少了回歸出錯(cuò)的可能性。而當(dāng)我們從測試那里獲得了一個(gè)bug時(shí),就可以通過測試用例去還原,當(dāng)我們這個(gè)測試通過后,這個(gè)bug也就解決了,而且這個(gè)bug fix的測試用例也保證了這個(gè)bug以后不會(huì)再次出現(xiàn)。
降低重構(gòu)難度
有了單元測試的保障,我們可以比較大膽的進(jìn)行重構(gòu)設(shè)計(jì),而單元測試也會(huì)成為重構(gòu)時(shí)很好的一個(gè)模具。當(dāng)然在重構(gòu)時(shí)也需要對單元測試進(jìn)行重構(gòu),但是和可靠性相比,這種額外的負(fù)擔(dān)是值得去承受的
減少調(diào)試時(shí)間
在調(diào)試中,我們很多時(shí)候都需要花費(fèi)一些額外的時(shí)間來觸發(fā)需要調(diào)試的代碼,但是在單元測試中,我們能夠針對需要調(diào)試的代碼構(gòu)建相關(guān)的測試用例,方便的進(jìn)行反復(fù)的測試與模擬,大大減少了調(diào)試的時(shí)間。
減少低級錯(cuò)誤
測試的存在價(jià)值就是為我們發(fā)現(xiàn)并解決錯(cuò)誤,單元測試更是如此,當(dāng)我們對自己的代碼進(jìn)行單元測試時(shí),就能容易的排除掉一些非常低級的錯(cuò)誤,起碼我們能夠保證在一些正常的情況下代碼是可以正確工作的。
描述代碼
好的代碼就是一份好的文檔,單元測試更是如此。一份好的單元測試能夠描述在對應(yīng)的情況下,代碼應(yīng)該有如何的預(yù)期表現(xiàn),那么別人只需要查看測試用例就能清楚的知道代碼的功能。
提高代碼質(zhì)量
一份代碼如果和其他代碼強(qiáng)耦合,它是難以被測試的,所以為了測試,開發(fā)人員會(huì)被驅(qū)使寫出低耦合、可擴(kuò)展的代碼。

三、單元測試方案
單元測試中有測試驅(qū)動(dòng)開發(fā)(TDD)與行為驅(qū)動(dòng)開發(fā)(BDD)兩種思路
測試驅(qū)動(dòng)開發(fā)(TDD)
- 根據(jù)需求與接口先編寫測試用例
- 根據(jù)測試用例編寫業(yè)務(wù)代碼
- 開發(fā)效率低
- 資源耗費(fèi)大
- 測試覆蓋率高
行為驅(qū)動(dòng)開發(fā)(BDD)
- 通過測試用例描述代碼行為
- 通過自動(dòng)運(yùn)行測試用例快速反饋
- 通過Mock作為相關(guān)代碼模塊的替身
- 開發(fā)效率較高
- 資源耗費(fèi)較低
- 測試覆蓋率較TDD要低
基于目前項(xiàng)目的情況與開發(fā)流程,我選擇了BDD作為測試框架,并會(huì)選擇使用XCTest + OCMock + OCHamcrest的方案,以下是三個(gè)框架的介紹:
XCTest
XCTest 可以完成的事
- 基本斷言的邏輯判斷
- 異步測試
- 性能測試
為什么選擇 XCTest
- XCode原生的測試框架,能夠更好適應(yīng)Apple之后的更新
- XCTest有大量文檔支持,上手難度較低
- XCTest添加進(jìn)項(xiàng)目后只是作為項(xiàng)目測試框架,并不會(huì)影響到打包等一些東西
OCMock
為什么需要 OCMock
mock即為模擬,OCMock可以偽造(模擬)一個(gè)對象,給它一些預(yù)設(shè)的值之類的,并進(jìn)行對應(yīng)的驗(yàn)證
比如在我需要測試WiFi直連模塊時(shí),我需要一個(gè)WiFi才能測試直連功能,這個(gè)時(shí)候我們就可以利用OCMock,去模擬一個(gè)WiFi對象,它可以是模擬成風(fēng)險(xiǎn)WiFi,也可以模擬成免費(fèi)WiFi,這樣我們的直連模塊的測試就完全獨(dú)立于WiFi對象,可以方便的進(jìn)行測試。
OCMock 可以完成的事
- 創(chuàng)建一個(gè)模擬對象,模擬一個(gè)特定對象的行為,排除一些外部類的干擾
- 構(gòu)造自己的用例進(jìn)行驗(yàn)證
- 對已有方法進(jìn)行重定義,以自己定義的邏輯進(jìn)行交互
- 判斷函數(shù)是否執(zhí)行過
為什么選擇 OCMock
- 原生XCTest并不支持Mock功能
- OCMock是專門為iOS與OS X進(jìn)行Mock測試的開源項(xiàng)目,擁有超過5000+ app使用,1100萬+下載量
- OCMock使用Apache 2.0協(xié)議,能夠在需要時(shí)候修改代碼滿足需要并作為開源或商用產(chǎn)品發(fā)布/銷售
- OCMock有官方文檔,資料齊全
OCHamcrest
OCHamcrest 可以完成的事
- 更高級的斷言
- 斷言可擴(kuò)展性
- 支持結(jié)構(gòu)體的斷言
為什么選擇 OCHamcrest
- 相對于另一個(gè)斷言框架 Expecta ,OCHamcrest更為成熟,Expecta可能會(huì)導(dǎo)致斷言結(jié)果錯(cuò)誤
- XCTest 內(nèi)置斷言并不充分,復(fù)雜條件下的判斷需要編寫大量斷言代碼
- 利用OCHamcrest的擴(kuò)展性能夠?qū)⒏袷交淖远xlog輸出到日志文件,提供更多可以定制化而且詳細(xì)的信息
四、整體測試框架

五、應(yīng)該測試什么 應(yīng)該怎么測試
測試的原則
- 快速:這樣才不會(huì)介意去運(yùn)行
- 獨(dú)立:一個(gè)測試不應(yīng)該耦合于另一個(gè)測試
- 可重復(fù):每次測試的結(jié)果應(yīng)該一致
- 可驗(yàn)證:結(jié)果應(yīng)該是成功/失敗,而不是一個(gè)解釋性的日志文檔
測試的內(nèi)容
在寫任何測試前,應(yīng)該明確應(yīng)該要測試什么,一般的情況下,單元測試應(yīng)該包括這些內(nèi)容:
- 核心功能測試
- 邊界條件
- 錯(cuò)誤處理
測試的思路
針對目前項(xiàng)目情況,我會(huì)使用單元測試與人工測試相結(jié)合的方式去進(jìn)行,因?yàn)槟壳拔覀兇蟛糠止δ芏际桥cUI牽連,不能完全依靠單元測試去完成所有的測試工作,但是我們可以將邏輯部分進(jìn)行分離,舉網(wǎng)絡(luò)連接模塊為例:
請求流程

我們可以對界面相關(guān)的部分測試進(jìn)行拆解,在邏輯部分實(shí)現(xiàn)單元測試,而人工測試部分單純檢查整個(gè)直連流程和界面部分是否正常。
這樣能夠避免人工測試時(shí)依賴于邏輯的情況,比如在需要測試發(fā)起100個(gè)請求后模塊是否會(huì)出現(xiàn)問題時(shí),無需依靠手工去真的連接100次,只需要在單元測試中模擬進(jìn)行100次連接,并查看結(jié)果是否正確就可以。
應(yīng)該測試的對象
在項(xiàng)目中,我們有大量的類,全部覆蓋單元測試是不現(xiàn)實(shí)的,我們需要進(jìn)行挑選。以下是我列舉的一些因素。
1.數(shù)據(jù)相關(guān)
比如在本地?cái)?shù)據(jù)存儲(chǔ)模塊中,我們需要保存不同的數(shù)據(jù),這時(shí)候我們可以通過單元測試構(gòu)造不同的測試數(shù)據(jù)進(jìn)行保存,查看是否保存成功,數(shù)據(jù)部分是單元測試最需要覆蓋的部分。
2.邏輯相關(guān)
比如在連接模塊中,需要對部分請求結(jié)果進(jìn)行過濾,這就是一個(gè)邏輯,針對這種邏輯,可以在單元測試中進(jìn)行測試是否過濾成功,而人工測試則無需關(guān)注過濾的邏輯,僅僅需要關(guān)注過濾后界面是否正常顯示。
3.多狀態(tài)的模塊
比如在連接模塊中,連接的狀態(tài)就有8種,包括了連通性檢查、連接中、已連接等,這些狀態(tài)能夠利用單元測試很好的模擬出來,這樣就解決了人工測試下難以模擬不同狀態(tài)轉(zhuǎn)換的問題。
六、總結(jié)
我們寫代碼最終的目的只有兩個(gè):實(shí)現(xiàn)需求與提高代碼質(zhì)量,在保證完成需求的前提下,增加單元測試能提高代碼的質(zhì)量與可維護(hù)性,縱使在引入了單元測試后,我們也許會(huì)面臨增加了研發(fā)的代碼量,花費(fèi)更多精力在編寫單元測試上,增加了開發(fā)成本,但我認(rèn)為相比于單元測試帶來的優(yōu)勢,這些是能夠克服的。
以上內(nèi)容就是本篇的全部內(nèi)容以上內(nèi)容希望對你有幫助,有被幫助到的朋友歡迎點(diǎn)贊,評論。
如果對軟件測試、接口測試、自動(dòng)化測試、面試經(jīng)驗(yàn)交流。感興趣可以關(guān)注我,我們會(huì)有同行一起技術(shù)交流哦。