Grails 鼓勵各層面的程式碼重用:微觀層面有局部模板、TagLib、ExpandoMetaClass、服務與編解碼器;宏觀層面若需整合控制器、領域類別、服務等多重組件,插件即是最佳方案。Grails 官方入口網站已有 250 多個插件,本文將實作一個 ShortenUrl 插件,整合 TinyURL 與 Is.Gd 兩大縮網址服務,並示範測試、打包與部署全流程。
在空目錄執行 grails create-plugin shortenurl 產生插件骨架。根目錄的 ShortenurlGrailsPlugin.groovy 為插件描述檔,包含版本、相依 Grails 版本、作者、標題、描述等中繼資料。若要發布至官方入口,需完善這些資訊;本文作為私有插件使用,暫不修改。
插件核心為兩個 POGO(Plain Old Groovy Object)類別,置於 src/groovy/org/grails/shortenurl/ 套件下,避免與使用者專案類別衝突:
https://tinyurl.com/api-create.php?url=... 取得縮網址。URL.openConnection() 檢查 HTTP 回應碼,成功回傳縮網址,失敗回傳錯誤訊息,提供更完整錯誤處理。因涉及即時網路呼叫,屬整合測試。在 test/integration/org/grails/shortenurl/ 建立 TinyUrlTests 與 IsGdTests,繼承 GroovyTestCase 並設定 def transactional = false(插件無資料庫,避免交易管理器錯誤)。測試驗證已知長網址對應的縮網址,以及無效網址的錯誤處理。執行 grails test-app 確認通過。
執行 grails create-service ShortenUrl 產生服務,注入兩個工具類別,提供 tinyurl() 與 isgd() 方法。服務同樣設定 transactional = false。isgd() 會記錄錯誤日誌,利用 Grails 自動注入的 log 物件。
服務單元測試預設置於 test/unit/。因單元測試隔離執行,log 物件不會自動注入,導致 testIsGdWithBadUrl 失敗。解決方法:在 setUp() 呼叫 mockLogging(ShortenUrlService) 注入模擬記錄器(輸出至 System.out),測試即可通過。HTML 報告的 System.out 連結可查看模擬日誌輸出。
執行 grails package-plugin 產生 grails-shortenurl-0.1.zip。在另一空目錄建立測試專案 grails create-app foo,執行 grails install-plugin /path/to/grails-shortenurl-0.1.zip 安裝。application.properties 會記錄 plugins.shortenurl=0.1。
在測試專案建立 TestController,注入 shortenUrlService,提供 tinyurl 動作接受查詢參數 q 並回傳縮網址。啟動應用瀏覽 https://localhost:9090/foo/test/tinyurl?q=https://grails.org 即見縮網址結果,點擊可正確導向原網址。
開發 Grails 插件與開發應用程式幾乎相同:create-plugin 取代 create-app,package-plugin 取代 war,中間步驟(建立服務、撰寫測試)完全一致。本文並示範單元測試的 mockLogging() 技巧,下一篇將介紹 mockDomain()、mockForConstraintsTests() 等更強大的模擬方法。