APP重構(gòu)之路:引入單元測試

一、為什么要引入單元測試

在開發(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ù)交流哦。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容