在前幾天的文章中,我們知道了需求有很多種,有功能需求,有工程需求,有增加使用者體驗的需求 …等,測試肯定也不會少寫,那我們到底要寫多少測試才夠呢?畢竟隨著我們測試越寫越多,維護的成本也越來越高,今天就來聊聊測試是否夠多了的問題吧。
在單元測試中,有一些指標可以讓我們參考測試夠完整,其中測試覆蓋率是一個滿常見的指標,用來指出我們專案中的有多少行程式碼、多少類別是有被測試到的。
在 Flutter 中,我們可以下面指令來跑一次測試,並產出測試覆蓋率的報告。
flutter test --coverage
測試覆蓋率報告預設會放在專案根目錄中的 coverage 資料夾中。
有許多工具可以讀取覆蓋率報告檔案,這邊我們使用 Intellij Idea 來展示,打開工具列中,Run ⇒ Show Coverage Data,最後選擇讀取剛剛產生的 lcov 檔案,就能看到測試覆蓋率了。
除此之外,使用 macOS 的朋友也能安裝 lcov 套件,然後用 genhtml 命令來產生更網頁版的報告。
brew install lcov
genhtml coverage/lcov.info -o coverage/html
我們可以用測試覆蓋率來確認測試是不是夠完整,但是需要注意的是,測試覆蓋率並不適合作為唯一指標,畢竟我們很可能為了讓覆蓋率數字漂亮,會去寫許多意義不大的測試,或者在重要的地方只有一兩個測試。那除了測試覆蓋率之外,我們還有什麼指標可以使用呢?
我們可以回頭思考測試的目的是什麼?測試其中一個目的是增加產品的品質,所以我們可以觀察每週的 Bug 數量是否有變少的趨勢,或者是近幾次產品 Release 是否順利,出問題的次數有沒有變少,這些都是一些可以觀察的指標。
除此之外,開發人員的信心也是一項指標,可以想一想我們是否有足夠的信心在放假前 Release 產品。如果我們測試足夠完整,開發人員有信心通過測試的產品不太會有什麼大問題,願意在放假前 Release 產品,其實也是一種測試足夠完整的表現。
我們都知道自動化測試有很多種,有 UI 測試、有整合測試、也有單元測試 …等。不同的測試的測試目的也不相同,維護的成本自然也不相同。維護成本越高的測試,我們能寫的數量也會越少,大多時候是用來測試最重要的功能。以 UI 測試來說,雖然我們可以從使用者的角度來測試最真實的狀況,但也容易受到各種原因導致測試失敗,可能是功能真的有問題,也可能是網路波動導致 API 回錯造成測試失敗,開發人員得花時間檢查才能知道原因,這也是為什麼維護成本高的原因。越下層的測試執行起來越穩定,速度越快,維護成本較低,所以測試數量也能比較多,最後形成金字塔的形狀,也是比較健康的比例。
出處 https://martinfowler.com/bliki/TestPyramid.html
與測試金字塔相反的是冰淇淋測試,在下面圖片中,我們可以看到最大量的是手動測試,往下依序是數量最多 UI 測試、數量次之的整合測試、數量最少單元測試。當我們發現專案的測試數量比例是冰淇淋筒狀的時候,我們可能會花很多時間在維護測試,每次都需要執行大量的手動測試來確保產品品質,而無法透過單元測試來避免基本問題。
出處:https://abseil.io/resources/swe-book/html/ch14.html
當然解決方法也就跟處理遺留代碼的方式一樣,我們透過迭代的方式,逐步在專案中引入更多的單元測試,讓專案可以透過單元測試避免一些基本的問題,然後才在此之上用整合測試和 UI 測試保護重要流程。
在工具越來越優化,機器性能越來越好的今天,很多過去可能要花很久的而且不穩定的 UI 測試,用新的工具或框架來測試都能有效提高執行效率與穩定性。像是我們可以在本地端把所有前端、後端、資料庫都建起來,跑起 UI 測試就比較不會受到外在因素干擾,也會比較穩定,這在過去機器性能比較差的時候,可能比較難做到。
當我們的 UI 測試有一定的穩定度,那我們可能就會減少整合測試,畢竟如果 UI 測試穩定度與執行速度都能跟整合測試一樣,那我們就沒什麼理由做整合測試。像是在開發 Flutter 的時候,Widget Test 的數量是可以寫比較多的,因為 Widget Test 無論是執行速度或穩定性,其實運行起來速度都十分快,此時檢視測試數量之間的比例可能就會變成沙漏型。
其實無論是金字塔型、冰淇淋筒狀、或者是沙漏型,我們最終需要確保的是測試的維護成本是在合理的範圍,只要我們能使用最合適的方式,控制測試維護成本落在合理範圍,維持產品品質,最後我們的測試數量比例呈現什麼形狀倒不是太重要。
雖然我們整個系列都在談論單元測試和 Widget Test 等自動化測試,但其實自動化測試只能測試我們已經想到的情境,那些我們想都沒想過的情境,我們肯定也不會為它寫測試。如果專案不大,或許問題不大,但是隨著功能越加越多,需求越來越複雜,不同功能交叉在一起可能會有意想不到的問題發生,這些問題幾乎不太可能用自動化測試找出來。
除了我們常見的單元測試、整合測試、UI 測試,其實測試還有許多不同的測試方法,例如探索性測試、冒煙測試,不同的測試方法的目的與效果也不相同,可以把不同的測試想像成不同的網子,不同網子可以捕捉到的問題也不同,把各種不同的網子一起使用,最後真正能露出去問題就都只是小問題,產品品質也由此而生。
除了測試之外,我們也應該吃自己的狗糧,什麼是吃自己狗糧呢?簡單來說就是真正的去使用自己開發的產品。有時候,產品雖然不會出錯,但卻不好用,可能是流程不順暢,提示不明確 …等的使用者體驗問題,這些問題可能不會被歸類為 Bug,但是開發人員也應該要關心這些問題,畢竟我們寫程式的目的是解決使用者的問題,如果使用者最後因為體驗不佳而離開,我們寫的程式也失去了存在的意義。
無論是自動化測試,或手動測試,其最終的目的都是提昇產品品質,比起一昧使用單元測試,組合使用不同的測試方式,能更有效的達到目的。但是雖然我們可以使用各種不同的測試來測試產品,但是還是注意每一種測試方式的成本,透過調配不同測試之間的比例,才能讓在有限的時間裡發揮最大的效益。