l

2023年6月17日 星期六

你就是寫太多測試才會沒時間(1):證明自己的清白

June 17 16:31~18:24

▲圖1:單元測試驗證修改過的email是否正確 

 

前言

Teddy的朋友Kuma幾個月前寫了一本書:《你就是不寫測試才會沒時間:Kuma 的單元測試實戰 -- Java篇》。的確,自從Teddy在N年前開始寫第一個自動化單元測試之後,Teddy一直認為測試是開發不可分割的一部分。好的測試可以協助釐清規格、作為驗收條件、找出回歸錯誤,以及支持重構,讓開發人員走得更穩、更快。

但是,隨著測試案例越來越多,管理與重構這些測試案例就變成另一個頭痛的問題。下周二Teddy舉辦一個網路演講,講題是:「你就是寫太多測試才會沒時間」,就是要討論應對這種現象的方法。這個講題這雖然是一句帶有玩笑性質的話,但也代表對於測試看法的一種演進過程:

不寫測試沒時間 ---> 寫了測試有時間--->  累積太多欠管理的測試又變得沒時間 ---> 下階段是什麼?

針對這個主題,今天談一個單純一點的情況:如何簡單驗證待測程式沒有做它不該做的事情?

***

從範例看問題

軟體測試有一個基本原則:「除了要驗證待測程式做了該做的事情,也要驗證它沒有做不該做的事。」舉個例子,圖1中的User物件,呼叫它的changeEmail方法設定新的email,在第101行中驗證email是否被正確設定。這種測試很常見,但嚴格講起來這個測試並不完整。除了驗證email有被正確修改以外,還需要確保User物件的其他欄位沒有被改變。因為難保changeEmail的實作,除了改變email以外,會不會不小心動到User物件的其他欄位,例如把nickname清空。

但是,如果每一個測試案例都去驗證不應該被改動的欄位真的沒有被異動,將會增加很多測試工作,如圖2所示。


▲圖2:第104~109行驗證User除了email以外的其他欄位維持原狀 

***

這還只是單元測試而已,如果是Use Case層次的測試案例,例如ChangeEmailUseCase,從Repository讀出User之後,理論上相同的assertion還要再寫一次。除了需要花費而外時間撰寫測試,也造成duplication code,增加日後維護測試案例的成本。

***

解決方案

先講結論,Teddy使用AssertJ這個「Fluent assertions for java」的測試工具來解決這個問題。圖3為Teddy使用ezSpec(Teddy自行開發的BDD工具軟體,可以直接用Java寫Given-When-Then,過一陣子會開源)所撰寫的ChangeEmailUseCase測試,第73行透過Repository從資料庫中拿出修改過email的User物件,然後第74行比對email欄位是否被正確修改。

接著在第76~78行使用AspectJ的assertThat做為比對物件的方式,呼叫usingRecursiveComparison,然後透過ignoringFieldsMatchingRegexes指定那些欄位不需要比對,最後再呼叫isEqualTo,就可以排除特定欄位之後,比對兩個物件是否相等。

圖3的程式範例擷取自ezKanban,由於ezKanban支援樂觀鎖,因此在每一個Aggregate物件身上都有一個version欄位用以作為樂觀鎖使用。因為User物件是一個Aggregate,所以User的email改變之後,version數值加1,因此在第77行比對修改前與修改後的兩個User物件實例是否相等的時候,除了排除email,也要排除version。


▲圖3:採用ezSpec撰寫的ChangeEmailUseCase測試 

***

結論

透過工具幫忙,就可以用很簡潔的方式去確保物件的狀態。雖然Teddy在範例中使用AssertJ做為比對的工具,但相信不同的語言應該可以找到類似的工具。如果真的找不到怎麼辦?那就自己寫一個啊。

***

友藏內心獨白:開發人員就是要有Maker精神。

沒有留言:

張貼留言