2011年11月2日

敏捷不油膩的 Java:使用 Gradle 與 Spock 快速建立易寫易讀的測試案例及報表

先前曾經寫過兩篇 Gradle 的介紹:
  1. 敏捷 Java 開發之 Gradle 專案自動化工具介紹
  2. 使用Gradle快速建立Jetty網站開發測試環境
這次再提供一個簡單的案例,希望可以讓大家感受到 Gradle 帶來的便利。

如果我們希望軟體專案,可以更敏捷,測試絕對絕對絕對不可少。

沒有寫測試的代碼,除了每次都要用「工人智慧」進行「人工測試」外,可憐的程式設計師在加班爆肝腦袋打結的時候,還必須小心注意每一行新增或修改的 Code,是不是會影響到其他功能。

也就是說,當你為了明天的交貨進度趕程式加班到十點,程式改完之後,還必須把整個系統測試過一次;如果不幸發現問題,再改一次程式之後,又必須再把整個系統測試過一次。

雖然這聽起來真的很蠢,但很不幸的,很多軟體專案卻是這麼做(當然也可以都不要測試,然後牢記:「我早上測試明明都沒問題啊!」這句話)。

聰明的軟體專案主管,除了懂得找來新鮮肝的能者,還知道要請一位 Jenkins 管家,幫大家做持續整合的工作。

也就是說,每位開發人員完成一個段落,只要交付給版本控制系統。就可以開心地去泡杯咖啡,翻一下雜誌,或打個小盹。是的,只要把肝的良率顧好!

至於軟體專案的良率呢?吃苦耐勞的 Jenkins 管家一天工作24小時,他有絕對充足的時間,會負責盯著軟體的良率,並且把報告交給你。

好的測試,電腦是你的工人。沒有好的測試,你是電腦的奴隸。

雖然嘴砲很簡單,可是做起來好像很難,尤其是一開始專案很小很小就要做「測試」。

Java 一直都有很棒的單元測試工具(JUnit),可是我幾乎從來不用它。原因很簡單,因為這些測試工具用起來真是宇宙超級無敵大麻煩。

如果寫好一支程式需要花半小時,但為了加上測試,可能需要再多花半小時搞定一大堆相依套件、XML設定檔等等等,寫個測試案例居然還比寫程式主要功能複雜;寫好的測試案例別人有看沒有懂,自己過了一段時間可能也看不太懂,更糟糕的事情是,產生的測試報告,根本是給機器(或鬼)看的。

更糟糕的事情不僅如此,也許你花了很多時間搞定這個專案的自動化測試環境,但下一次碰新的專案,這一切幾乎又要重頭來過。

為了建立容易重複使用的 Java 自動化測試環境,我們用 Gradle 輕鬆解決。

我們唯一需要做的事情,就是在專案底下增加一個 build.gradle 檔案(當然必須先裝好 Gradle 軟體)。


這個設定啟用兩個 Plugin,分別是 Java 及 Groovy。Java Plugin 負責用來將 .java 原始碼編譯,Groovy Plugin 則負責編譯 .groovy (Spock 的測試案例使用 Groovy 撰寫)。


我們定義了一個非常簡單的 Hello 類別,包含一個 sayHello 方法,傳入的參數是字串型態的「名字」,例如傳入 John,這個方法就會回傳 「Hello, John」。


使用 Groovy 撰寫的 HelloSpec 類別,則定義「say hello to everyone」的測試案例。這個測試的 where 段落列舉多筆名字資料,以及預期 sayHello 方法會回傳的字串,在 expect 段落則會逐一進行 sayHello 的呼叫及比對回傳結果。

在這個例子中,我們需要做的事情非常精簡,就是先在一台裝好 Gradle 的機器上,建立一個 build.gradle 設定,並撰寫一個測試案例。並不需要下載一大堆相依套件(.jar檔),也沒有複雜的 XML 設定。

我們將這些檔案,依照 Gradle 的慣例,放在以下的資料夾路徑:
  • /build.gradle
  • /src/main/java/com/example/Hello.java
  • /src/test/groovy/HelloSpec.groovy
這是一個相當精簡的範例,因此我們實踐測試驅動開發的方法,在撰寫 Hello 的 sayHello 方法時,先在 HelloSpec 把測試案例寫好,這個測試案例就已經清楚描述 sayHello 方法的功能,當我們將 Hello 類寫好交給其他程式設計師,只需要附上測試案例,就已經清楚說明它的功能及使用方式。

有了 build.gradle 檔案,我們只要在終端機下執行一行指令:
gradle test

就會自動完成編譯,並執行測試案例,且最後的結果報告將以漂亮的網頁方式呈現。




如果測試失敗,可以從終端機看到訊息:

Test HelloSpec FAILED
1 test completed, 1 failure

不僅如此,測試報告也會有漂亮的錯誤說明:

灰色區塊的說明,就是 Spock 風格的測試輸出結果,可以清楚看到這個測試是比對 sayHello 與 words 的字串值,不但標示出哪個字元不一樣造成測試失敗,甚至連相似度(similarity)都幫你算出來,也清楚標示無法通過測試的問題發生在 HelloSpec 程式碼的第幾行位置。

如果程式執行過程中,有標準輸出(STDOUT即System.out)或標準錯誤輸出(STDERR即System.err),在測試報告中也會清楚呈現,因此非常容易幫助除錯。


從上面的例子,我們看到整個專案的自動化測試,簡化到只需要執行一行 gradle test 就搞定!

這麼做的意義,不只是為了讓日常的開發作業更省事,更重要的事情是:可以把專案測試的工作外包給 24 小時不打烊的 Jenkins 管家負責。

如果測試過程很複雜,又或者牽涉到許多需要操作 GUI 圖形介面工具,就很難用簡單的指令步驟自動化。Jenkins 雖然很勤勞,但是它不容易處理太複雜的步驟。

當整個專案測試簡化到只要一行 gradle test 時,Jenkins 管家就只需要負責:

  1. 從版本控制系統取出最新版程式碼
  2. 執行 gradle test 完成自動化測試 
  3. 記錄測試結果、將測試報告文件上傳儲存或 E-Mail 給指定人員

Jenkins 是 24  小時待命,你可以要求他每個小時都做一次測試,他不會有任何一句抱怨。

但前提是:執行測試的方法必須非常簡化。

如果我們沒有用 Gradle,就必須在每一台開發機器上都建置相同的環境,也就是那些必要的相依套件(.jar檔案)缺一不可,而且執行編譯、測試這些動作,指令及參數也都必須統一才行。

例如,若你使用某家有點軟的公司,那套很強大的開發工具(V啥S啥的),你可能會覺得點幾下滑鼠,也能完成很酷很炫的測試工作。那確實很厲害,但是要教會 Jenkins 老管家這麼時髦的伎倆並不容易,因此你必須自己做。

若只是要測試自己的專案,手動操作一下工具當然很容易。但是,當一個專案是由多人共同開發,你要完成的測試工作,並不只有自己的那一部分,你每次告一段落的修改,都必須先跟其他人整合成新的版本,完整做過一次測試才能算數。這種情況下,誰願意當那位 Jenkins 管家呢?(偶爾 cosplay 一下沒關係,但天天做?殺了我吧!)

如果你是 Java 程式設計師,想要用更敏捷的方式開發軟體,那麼絕對不要錯過 Groovy、Scala,以及受它們所啟發的各項技術。

本文的範例程式碼:


2 則留言:

  1. cool!有機會跟你請教一下~

    回覆刪除
  2. 好哦,有空研究一下gradle+jasperreport

    回覆刪除

lyhcode by lyhcode
歡迎轉載,請務必註明出處!