IOS-APP重構之路(三) 引入單元測試
-
IOS-APP重構之路(一) 網路請求框架
-
IOS-APP重構之路(二) Model的設計
重構的時候我們需要一個模具,讓我們能夠大膽修改的同時確保結果的正確性,這個時候就要引入“單元測試”了。
前言
本文沒有給出任何測試程式碼,或者是在教你如何編寫一份具有良好測試性的程式碼,而是闡述在重構過程中單元測試的重要性與實現方法,關於程式碼可測試性相關的內容我會另開一篇文章去具體闡述。(畫個餅)
一、為什麼要引入單元測試
在開發過程中我們會遇到這樣一些問題:
-
面對需要重構龐大的模組程式碼時無從下手
-
修改了一處地方卻在另一處地方引發了新的bug
-
擴充套件新功能的同時導致舊程式碼出現bug
-
在測試人員難以覆蓋到的基礎功能介面出現了bug
-
出現了一種難以重現的特殊邊界條件觸發的bug
另外我們也許還會遇到一些這樣的模組:
-
A模組依賴於B模組的結果,但是B模組尚未開發完成
-
模組狀態過於複雜,手工測試需要耗費大量時間
-
模組業務與時間節點相關,手工測試難以覆蓋
這個時候也許能夠利用經驗和豐富的debug技巧來解決這些問題,但是很多時候我們的處理並不完美,因為我們缺少了一個規範,在編碼過程中難以顧及其他模組的影響,這個時候,我們就需要引入單元測試。
二、單元測試的價值
可維護性增強
當在對程式碼進行修改時,利用單元測試就能夠清晰的知道是否破壞了老的業務邏輯,這樣大大減少了迴歸出錯的可能性。而當我們從測試那裡獲得了一個bug時,就可以通過測試用例去還原,當我們這個測試通過後,這個bug也就解決了,而且這個bug fix的測試用例也保證了這個bug以後不會再次出現。
降低重構難度
有了單元測試的保障,我們可以比較大膽的進行重構設計,而單元測試也會成為重構時很好的一個模具。當然在重構時也需要對單元測試進行重構,但是和可靠性相比,這種額外的負擔是值得去承受的
減少除錯時間
在除錯中,我們很多時候都需要花費一些額外的時間來觸發需要除錯的程式碼,但是在單元測試中,我們能夠針對需要除錯的程式碼構建相關的測試用例,方便的進行反覆的測試與模擬,大大減少了除錯的時間。
減少低階錯誤
測試的存在價值就是為我們發現並解決錯誤,單元測試更是如此,當我們對自己的程式碼進行單元測試時,就能容易的排除掉一些非常低階的錯誤,起碼我們能夠保證在一些正常的情況下程式碼是可以正確工作的。
描述程式碼
好的程式碼就是一份好的文件,單元測試更是如此。一份好的單元測試能夠描述在對應的情況下,程式碼應該有如何的預期表現,那麼別人只需要檢視測試用例就能清楚的知道程式碼的功能。
提高程式碼質量
一份程式碼如果和其他程式碼強耦合,它是難以被測試的,所以為了測試,開發人員會被驅使寫出低耦合、可擴充套件的程式碼。
三、單元測試方案
單元測試中有測試驅動開發(TDD)與行為驅動開發(BDD)兩種思路
測試驅動開發(TDD)
-
根據需求與介面先編寫測試用例
-
根據測試用例編寫業務程式碼
-
開發效率低
-
資源耗費大
-
測試覆蓋率高
行為驅動開發(BDD)
-
通過測試用例描述程式碼行為
-
通過自動執行測試用例快速反饋
-
通過Mock作為相關程式碼模組的替身
-
開發效率較高
-
資源耗費較低
-
測試覆蓋率較TDD要低
基於目前專案的情況與開發流程,我選擇了BDD作為測試框架,並會選擇使用XCTest + OCMock + OCHamcrest的方案,以下是三個框架的介紹:
XCTest ios開發稽核交流群 869685378 歡迎各位大牛來分享交流 IOS,馬甲包,低要求,內容開發沒有限制,報酬豐厚,實力誠信 Q:782675105
XCTest 可以完成的事
-
基本斷言的邏輯判斷
-
非同步測試
-
效能測試
為什麼選擇 XCTest
-
XCode原生的測試框架,能夠更好適應Apple之後的更新
-
XCTest有大量文件支援,上手難度較低
-
XCTest新增進專案後只是作為專案測試框架,並不會影響到打包等一些東西
OCMock
為什麼需要 OCMock
mock即為模擬,OCMock可以偽造(模擬)一個物件,給它一些預設的值之類的,並進行對應的驗證
比如在我需要測試WiFi直連模組時,我需要一個WiFi才能測試直連功能,這個時候我們就可以利用OCMock,去模擬一個WiFi物件,它可以是模擬成風險WiFi,也可以模擬成免費WiFi,這樣我們的直連模組的測試就完全獨立於WiFi物件,可以方便的進行測試。
OCMock 可以完成的事
-
建立一個模擬物件,模擬一個特定物件的行為,排除一些外部類的干擾
-
構造自己的用例進行驗證
-
對已有方法進行重定義,以自己定義的邏輯進行互動
-
判斷函式是否執行過
為什麼選擇 OCMock
-
原生XCTest並不支援Mock功能
-
OCMock是專門為iOS與OS X進行Mock測試的開源專案,擁有超過5000+ app使用,1100萬+下載量
-
OCMock使用Apache 2.0協議,能夠在需要時候修改程式碼滿足需要並作為開源或商用產品釋出/銷售
-
OCMock有官方文件,資料齊全
OCHamcrest
OCHamcrest 可以完成的事
-
更高階的斷言
-
斷言可擴充套件性
-
支援結構體的斷言
為什麼選擇 OCHamcrest
-
相對於另一個斷言框架 Expecta ,OCHamcrest更為成熟,Expecta可能會導致斷言結果錯誤
-
XCTest 內建斷言並不充分,複雜條件下的判斷需要編寫大量斷言程式碼
-
利用OCHamcrest的擴充套件效能夠將格式化的自定義log輸出到日誌檔案,提供更多可以定製化而且詳細的資訊
四、整體測試框架
五、應該測試什麼 應該怎麼測試
測試的原則
-
快速:這樣才不會介意去執行
-
獨立:一個測試不應該耦合於另一個測試
-
可重複:每次測試的結果應該一致
-
可驗證:結果應該是成功/失敗,而不是一個解釋性的日誌文件
測試的內容
在寫任何測試前,應該明確應該要測試什麼,一般的情況下,單元測試應該包括這些內容:
-
核心功能測試
-
邊界條件
-
錯誤處理
測試的思路
針對目前專案情況,我會使用單元測試與人工測試相結合的方式去進行,因為目前我們大部分功能都是與UI牽連,不能完全依靠單元測試去完成所有的測試工作,但是我們可以將邏輯部分進行分離,舉網路連線模組為例:
請求流程
我們可以對介面相關的部分測試進行拆解,在邏輯部分實現單元測試,而人工測試部分單純檢查整個直連流程和介面部分是否正常。
這樣能夠避免人工測試時依賴於邏輯的情況,比如在需要測試發起100個請求後模組是否會出現問題時,無需依靠手工去真的連線100次,只需要在單元測試中模擬進行100次連線,並檢視結果是否正確就可以。
應該測試的物件
在專案中,我們有大量的類,全部覆蓋單元測試是不現實的,我們需要進行挑選。以下是我列舉的一些因素。
1.資料相關
比如在本地資料儲存模組中,我們需要儲存不同的資料,這時候我們可以通過單元測試構造不同的測試資料進行儲存,檢視是否儲存成功,資料部分是單元測試最需要覆蓋的部分。
2.邏輯相關
比如在連線模組中,需要對部分請求結果進行過濾,這就是一個邏輯,針對這種邏輯,可以在單元測試中進行測試是否過濾成功,而人工測試則無需關注過濾的邏輯,僅僅需要關注過濾後介面是否正常顯示。
3.多狀態的模組
比如在連線模組中,連線的狀態就有8種,包括了連通性檢查、連線中、已連線等,這些狀態能夠利用單元測試很好的模擬出來,這樣就解決了人工測試下難以模擬不同狀態轉換的問題。
六、總結
我們寫程式碼最終的目的只有兩個:實現需求與提高程式碼質量,在保證完成需求的前提下,增加單元測試能提高程式碼的質量與可維護性,縱使在引入了單元測試後,我們也許會面臨增加了研發的程式碼量,花費更多精力在編寫單元測試上,增加了開發成本,但我認為相比於單元測試帶來的優勢,這些是能夠克服的。
相關文章
- APP重構之路(三) 引入單元測試APP
- 程式碼重構與單元測試——“提取方法”重構(三)
- 程式碼重構與單元測試——重構1的單元測試(四)
- iOS-APP重構之路(二) Model的設計iOSAPP
- ios-APP重構之路(一) 網路請求框架iOSAPP框架
- 程式碼重構與單元測試(一)
- 程式碼重構與單元測試——測試專案(二)
- Flutter 學習之路 - 測試(單元測試,Widget 測試,整合測試)Flutter
- 實驗三:單元測試
- 程式碼重構與單元測試——對方法的引數進行重構(五)
- 軟體測試實驗三單元測試
- Laravel 單元測試實戰(4)- 測試成功後的方法重構並再次測試透過Laravel
- 實驗三junit單元測試
- 測試 之Java單元測試、Android單元測試JavaAndroid
- 程式碼重構與單元測試——重構6:使用“多型”取代條件表示式(九)多型
- 單元測試:單元測試中的mockMock
- 搞定Go單元測試(三)—— 斷言(testify)Go
- 單元測試,只是測試嗎?
- 單元測試-【轉】論單元測試的重要性
- SpringBoot單元測試Spring Boot
- python 單元測試Python
- iOS 單元測試iOS
- Flutter 單元測試Flutter
- 單元測試 Convey
- 單元測試真
- golang單元測試Golang
- 單元測試工具
- 前端單元測試前端
- 十五、單元測試
- Go單元測試Go
- 聊聊單元測試
- 前端測試:Part II (單元測試)前端
- android-MVP架構中Presenter的單元測試AndroidMVP架構
- 程式碼重構與單元測試——使用“以查詢取代臨時變數”再次對Statement()方法進行重構(七)變數
- JavaScript單元測試框架JavaScript框架
- 單元測試 -- mocha + chaiAI
- React元件單元測試React元件
- Spring Boot 單元測試Spring Boot