APP重構之路(三) 引入單元測試


重構的時候咱們須要一個模具,讓咱們可以大膽修改的同時確保結果的正確性,這個時候就要引入「單元測試」了。xcode

前言

本文沒有給出任何測試代碼,或者是在教你如何編寫一份具備良好測試性的代碼,而是闡述在重構過程當中單元測試的重要性與實現方法,關於代碼可測試性相關的內容我會另開一篇文章去具體闡述。(畫個餅)網絡

1、爲何要引入單元測試

在開發過程當中咱們會遇到這樣一些問題:app

  • 面對須要重構龐大的模塊代碼時無從下手
  • 修改了一處地方卻在另外一處地方引起了新的bug
  • 擴展新功能的同時致使舊代碼出現bug
  • 在測試人員難以覆蓋到的基礎功能接口出現了bug
  • 出現了一種難以重現的特殊邊界條件觸發的bug

另外咱們也許還會遇到一些這樣的模塊:框架

  • A模塊依賴於B模塊的結果,可是B模塊還沒有開發完成
  • 模塊狀態過於複雜,手工測試須要耗費大量時間
  • 模塊業務與時間節點相關,手工測試難以覆蓋

這個時候也許可以利用經驗和豐富的debug技巧來解決這些問題,可是不少時候咱們的處理並不完美,由於咱們缺乏了一個規範,在編碼過程當中難以顧及其餘模塊的影響,這個時候,咱們就須要引入單元測試異步

2、單元測試的價值

可維護性加強

當在對代碼進行修改時,利用單元測試就可以清晰的知道是否破壞了老的業務邏輯,這樣大大減小了迴歸出錯的可能性。而當咱們從測試那裏得到了一個bug時,就能夠經過測試用例去還原,當咱們這個測試經過後,這個bug也就解決了,並且這個bug fix的測試用例也保證了這個bug之後不會再次出現。函數

下降重構難度

有了單元測試的保障,咱們能夠比較大膽的進行重構設計,而單元測試也會成爲重構時很好的一個模具。固然在重構時也須要對單元測試進行重構,可是和可靠性相比,這種額外的負擔是值得去承受的性能

減小調試時間

在調試中,咱們不少時候都須要花費一些額外的時間來觸發須要調試的代碼,可是在單元測試中,咱們可以針對須要調試的代碼構建相關的測試用例,方便的進行反覆的測試與模擬,大大減小了調試的時間。

減小低級錯誤

測試的存在價值就是爲咱們發現並解決錯誤,單元測試更是如此,當咱們對本身的代碼進行單元測試時,就能容易的排除掉一些很是低級的錯誤,起碼咱們可以保證在一些正常的狀況下代碼是能夠正確工做的。

描述代碼

好的代碼就是一份好的文檔,單元測試更是如此。一份好的單元測試可以描述在對應的狀況下,代碼應該有如何的預期表現,那麼別人只須要查看測試用例就能清楚的知道代碼的功能。

提升代碼質量

一份代碼若是和其餘代碼強耦合,它是難以被測試的,因此爲了測試,開發人員會被驅使寫出低耦合、可擴展的代碼。

3、單元測試方案

單元測試中有測試驅動開發(TDD)與行爲驅動開發(BDD)兩種思路

測試驅動開發(TDD)

  • 根據需求與接口先編寫測試用例
  • 根據測試用例編寫業務代碼
  • 開發效率低
  • 資源耗費大
  • 測試覆蓋率高

行爲驅動開發(BDD)

  • 經過測試用例描述代碼行爲
  • 經過自動運行測試用例快速反饋
  • 經過Mock做爲相關代碼模塊的替身
  • 開發效率較高
  • 資源耗費較低
  • 測試覆蓋率較TDD要低

基於目前項目的狀況與開發流程,我選擇了BDD做爲測試框架,並會選擇使用XCTest + OCMock + OCHamcrest的方案,如下是三個框架的介紹:

XCTest

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輸出到日誌文件,提供更多能夠定製化並且詳細的信息

4、總體測試框架

5、應該測試什麼 應該怎麼測試

測試的原則

  • 快速:這樣纔不會介意去運行
  • 獨立:一個測試不該該耦合於另外一個測試
  • 可重複:每次測試的結果應該一致
  • 可驗證:結果應該是成功/失敗,而不是一個解釋性的日誌文檔

測試的內容

在寫任何測試前,應該明確應該要測試什麼,通常的狀況下,單元測試應該包括這些內容:

  • 核心功能測試
  • 邊界條件
  • 錯誤處理

測試的思路

針對目前項目狀況,我會使用單元測試與人工測試相結合的方式去進行,由於目前咱們大部分功能都是與UI牽連,不能徹底依靠單元測試去完成全部的測試工做,可是咱們能夠將邏輯部分進行分離,舉網絡鏈接模塊爲例:

請求流程

咱們能夠對界面相關的部分測試進行拆解,在邏輯部分實現單元測試,而人工測試部分單純檢查整個直連流程和界面部分是否正常。

這樣可以避免人工測試時依賴於邏輯的狀況,好比在須要測試發起100個請求後模塊是否會出現問題時,無需依靠手工去真的鏈接100次,只須要在單元測試中模擬進行100次鏈接,並查看結果是否正確就能夠。

應該測試的對象

在項目中,咱們有大量的類,所有覆蓋單元測試是不現實的,咱們須要進行挑選。如下是我列舉的一些因素。

1.數據相關

好比在本地數據存儲模塊中,咱們須要保存不一樣的數據,這時候咱們能夠經過單元測試構造不一樣的測試數據進行保存,查看是否保存成功,數據部分是單元測試最須要覆蓋的部分。

2.邏輯相關

好比在鏈接模塊中,須要對部分請求結果進行過濾,這就是一個邏輯,針對這種邏輯,能夠在單元測試中進行測試是否過濾成功,而人工測試則無需關注過濾的邏輯,僅僅須要關注過濾後界面是否正常顯示。

3.多狀態的模塊

好比在鏈接模塊中,鏈接的狀態就有8種,包括了連通性檢查、鏈接中、已鏈接等,這些狀態可以利用單元測試很好的模擬出來,這樣就解決了人工測試下難以模擬不一樣狀態轉換的問題。

6、總結

咱們寫代碼最終的目的只有兩個:實現需求與提升代碼質量,在保證完成需求的前提下,增長單元測試能提升代碼的質量與可維護性,縱使在引入了單元測試後,咱們也許會面臨增長了研發的代碼量,花費更多精力在編寫單元測試上,增長了開發成本,但我認爲相比於單元測試帶來的優點,這些是可以克服的。

更多內容能夠關注個人博客

相關文章
相關標籤/搜索