關於 單元測試

Nalydadad
iOS Nerd 🤓
Published in
5 min readApr 8, 2018

最近在準備有關單元測試的簡報,有感而發,來談談我所認知的單元測試,以及我認為對一個軟體工程師來說,單元測試所需最基本的認知。今天不討論程式碼,純粹分享一些想法及概念。

“單元測試” 是什麼?

測試程式裡面可執行的最小單元,即為單元測試。而程式裡面最小可執行的單元為方法(function),所以單元測試是在測試方法的行為是否符合預期。除此之外,在物件導向的世界中,我們通常測試的目標會是一個類別(class),透過測試類別的行為,我們會產生一個測試套件,將這些測試案例(test case)放在測試套件(test suite)中,由此脈絡你可以很清楚的了解,你平常看到的單元測試是用什麼樣的一個邏輯設計出來的。

為什麼需要單元測試?

當然是買保險啊!第一次聽到這個名詞,應該都是這個反應,又或許你的腦海中已經浮現出許多答案。但我第一次對於買保險這個想法,其實是充滿疑問的, “單元測試也是人寫出來的程式碼,既然人寫出來的程式碼會有問題,為什麼要自己寫程式碼來測試自己的程式碼?”

現在想想覺得蠻有趣的,或許有人有一樣的疑惑,但我要以這段話來支持我的論點。既然你寫的單元測試也有可能出錯,那一定在測試的時候會反映出來,所以不需害怕測試程式碼讓你的測試失敗,應當仔細檢視失敗原因,且提交的時候,一定要確保所有的單元測試都是成功的,這樣才真正是一個保險。

它是一個保險,它能防守未來的你或一起共事的同事在重構或改動程式碼的時候不出錯。它也是一個 Spec,在設計測試案例的時候,同時在擬定這個類別應當有的行為,當你提交這段程式碼時,測試案例就是你所訂定的標準。

講到這就一定要提到之前同事常舉的例子。有一次他要使用某個第三方的 Framework,但官方文件看來看去都不知道怎麼使用,最後他從 Framework 的測試案例裡才學會如何使用。

掌握 3A 原則、一次只測一個行為

測試案例必須由三個部分組成, ArrangeActAssert ,就像是寫作文的起承轉合一樣。Arrange 描述這個受測目標的前置準備(生成物件)需有什麼條件;Act 描述要做什麼動作;以及 Assert 預期要有什麼行為。掌握這個思路,寫單元測試才會完整。測試程式的可讀性才會高,是為將來的你或是一起工作的同事提高維護的便利性。

除此之外,也請謹守一次只測試一個行為的原則。 就像是以前在實驗課學到的,必須確保實驗組及對照組的操縱變因只有一個,當測試不通過時,便能快速發現問題的根源,而不需要再做另外的測試。

謹守這兩個原則,你就可以開始撰寫簡單的單元測試。

解決相依性

當你開始寫單元測試,你可能會發現你寫不下去。在 Arrange 的階段,要準備的參數,就讓你千頭萬緒理不清。原本在設計的時候,過多的相依性,在這個時候將會全部糾結在一起,成為你的絆腳石。

這是不是也點出一個寫測試的好處?為了讓類別可以被測試(testable),我們更需在類別設計階段,仔細檢視類別之間的相依性,思考清楚類別之間的權責,以及開口(造成相依的原因)的設計。

除此之外,也有一些解決相依問題的 Pattern (Stub、Mock、Fake),雖然不在我們今天的討論範圍,但都是解決相依性的好方法。

在測試的過程,我有一個心法。我會把待測試物件想像成一顆心臟,當一顆心臟從身體拔出時,我要如何讓他繼續跳動,我必須把左心房以及右心房接上我自己模擬的假動脈,把左心室及右心室接上模擬的假靜脈,用幫浦打血液進假動脈,並測試假靜脈是否正常流出血液。這就是我如何讓原本相依的元件抽離的心法。

我要測到什麼程度?覆蓋率重要嗎?private 方法要測嗎?

回想一下你寫單元測試的初衷,一定是要確保你提交出去的程式碼保有你預期的行為,所以你才會在測試程式中 “插了許多眼” 。這樣答案是否就呼之欲出了,如果是你想要確保的行為,你是否就應該為它寫測試?

至於覆蓋率重要嗎?我覺得重質不重量,覆蓋率是要到一定的比例,代表你為這個類別大部分的程式碼提出擔保,但不一定要為了提高覆蓋率而走火入魔,我的做法其實是我不會特別注意覆蓋率,但我一定確保所有 public 方法都有被測到,或許你也可以以這為標準。同時也間接回應了第三個問題,反之 private 方法我不會測,因為既然它是 private,在類別內部的運作中,它必然會被呼叫到,所以執行了 public 方法,也同時在確保 private 方法不會 crash。

結論

我覺得寫單元測試是 Z>B,在開發階段,可以確保自己的設計邏輯正確,可以讓設計的架構清楚(至少耦合度較低),且為將來的自己準備好描述文件,甚至將來要改動這段程式碼時,有一個保障。當然有人會質疑,在開發階段是否會花更多的時間,而拖慢開發速度?我的答案是一定會,但會減少解 bug 的量及時間,讓程式更加穩定,當你熟悉且有良好習慣的時候絕對是能增加開發速度的。

不管在看這篇文章的你對於單元測試原本抱持什麼樣的立場,但希望這篇簡單的分享,有增加想要寫單元測試的動力,自己的程式碼品質自己救。希望看完此篇文章,對你有一些好的衝擊,也歡迎一起討論。

--

--