從我畢業第一份工作開始,就一直對測試很有興趣。因為當初菜鳥剛進入大公司做的工作很多是找 bug 跟救火,每次有時間壓力又找不到問題所在的時候都很痛苦。一直覺得「應該有更好的做法」,然後想去找其他處理過的人最後的結論是什麼。後來找到的參考資料是(Working Effectively with Legacy Code),雖然書的標題裡面沒有寫到測試兩個字,但是這本書在首章就開宗明義就說:
To me, legacy code is simply code without tests
雖然我認同書中闡述的概念,但是因為前東家不只自製引擎,還自製語言,改造過的 make 沒有文件根本不知道要怎麼拿去編譯遊戲本體以外的程式。所以到離職前都還沒有成功寫過一個測試。
回台灣之後就一直是在使用 Unity,一開始 Unity 是沒有測試這個概念的。到 Unity 4 末期才開始有 Unity Test Tool 以 Asset Store 獨立套件存在(現已下架),之後慢慢成為 Unity 5 的一部分。Unity 官方也有釋出一些文件倡導該怎麼使用:
-
Unit testing part 1 – Unit tests by the book
https://blogs.unity3d.com/2014/05/21/unit-testing-part-1-unit-tests-by-the-book/
(中文翻譯 by NaCl:https://fredxxx123.wordpress.com/2014/07/09/%E8%AD%AFunity-unit-testing-part-1-unit-tests-by-the-book/) -
Unit testing part 2 – Unit testing MonoBehaviours
https://blogs.unity3d.com/2014/06/03/unit-testing-part-2-unit-testing-monobehaviours/ -
Unit testing at the speed of light with Unity Test Tools
https://blogs.unity3d.com/2014/07/28/unit-testing-at-the-speed-of-light-with-unity-test-tools/
(中文翻譯 by Unity Taiwan:http://unitytaiwan.blogspot.tw/2014/08/unityunity-test-tools.html)
但是 Unity 提供的測試環境最大的問題就在於整個撰寫到測試(或是測試到撰寫,如果你是走 TDD)的過程太慢了。一般的步驟大概像是:
- 在編輯器裡改一點程式或是測試
- 視窗切到 Unity
- 等 Unity 編譯轉圈圈
- 打開 Unity Unit Test Runner(如果已經打開要關掉重開,因為編輯器視窗在編譯完成 Assembly Reload 後會爛掉)
- 執行測試
雖然整個過程一次沒有很久,但是當你要頻繁地使用測試驗證或是 TDD 時就會累積起來嚴重破壞節奏。雖然試著硬著頭皮逼自己在這樣環境下 TDD,但是大量的細碎的等待時間造成注意力發散,最後因為產出降低到自己受不了而放棄。
在嘗試失敗後有很長一陣子覺得 Unity 應該是不能做測試或是 TDD,而又走回原來的老路。只是後來在拉板凳看 DHH 跟 Kent Beck 還有 Martin Fowler 的「Is TDD Dead?」大戰時,碰巧看到別的影片在教用 Ruby TDD。看到他們用的跟 IDE 整合的 Test Runner 才發現原來別人都是這樣玩的,不是在那邊切來切去。
經歷過一些嘗試與失敗,再被 Unity 搞之後,終於發現使用 ReSharper 或是 Rider 的 Test Runner 跳過 Unity Editor 執行 Unity 測試的方法。
首先要確定你的 Unity 版本,Unity 5.5 與之前的版本使用的是 NUnit 2.6.4.0,這個版本是可以正常被 ReSharper 跟 Rider 識別的。從 5.6 開始 Unity 改用 NUnit 3.5,但是是用 .Net Standard 環境編譯(JetBrains issue:https://youtrack.jetbrains.com/issue/RIDER-6711#comment=27-2346418&u=1499686359938),所以 ReSharper 跟 Rider 無法對接。後來的解決方法是使用 Unity 的 OnGeneratedCSProjectFiles
callback,在 Unity 生成 C# 專案時覆寫 nunit.framework 的路徑。從 Unity 安裝目錄的版本改成 NUnit 官方發行版本。最近幾天把自己寫的這個 Post-processor 上傳到 GitHub:
https://github.com/FrankNine/UnityNUnitReferenceOverride
但是去跟 JetBrians 的人獻寶的時候才發現 Rider for Unity plugin 已經在上個月實作這個功能了。我自己測試過 Rider 2017.3 之後已經不需要另外裝這個 Post-processor,所以這個就只有舊版 Rider 跟 ReSharper 需要了。
處理好 nunit.framework 的連結之後在 Rider 裡就可以直接編譯執行測試,如果有必要可以設定 Session 只執行一部分的測試:
測試左邊的綠色勾勾代表測試通過。
我自己的慣用環境是 ReSharper,Rider 因為是比較晚出的產品所以支援還沒有 ReSharper 那麼好。ReSharper 除了可以在 Visual Studio 環境內執行 Unit test 之外,還可以另外加裝 dotCover:
亮綠色的部分代表測試有涵蓋(Coverage)到的程式,執行測試後可以用來觀察程式有哪些分支沒有被元件測試到。同時加裝 dotCover 後 Unit test 會多一項 Continuous Testing Session,可以把測試加到這裡然後選觸發條件 On ‘Save’ Build and Run Dirty Tests 這樣在儲存的時候 ReSharper 就會自動偵測兩次存檔間有影響到哪些測試,自動建置與執行測試,在執行測試的同時還可以繼續編輯不用停下來。最近在這樣的配置下用 TDD 開發一個遊戲內的功能,覺得還蠻順手的。如果有在開發 Unity 又對測試有興趣的可以試用看看。而給 Rider 用的 dotCover 還在開發中,預計 2018 上市,Mac 的使用者可能要再等等。
附記
關於測試本身我覺得自己的經驗還不夠,所以就先只介紹自己發現好用的工具。測試的概念可以參考:
-
30 天快速上手 TDD by 91
https://dotblogs.com.tw/hatelove/archive/2013/01/11/learning-tdd-in-30-days-catalog-and-reference.aspx -
The Art of Unit Testing: with examples in C#
https://www.amazon.com/Art-Unit-Testing-examples/dp/1617290890
這本書最近有出中文譯本,譯者一樣是 91
如果覺得 ReSharper 執行速度太慢,可以參考官方文件:
https://rider-support.jetbrains.com/hc/en-us/articles/207803955-Where-are-the-ReSharper-cache-files-kept- 尋找 cache 資料夾並將它加到防毒軟體的例外,對速度會有幫助。
最後重複在 UnityNUnitReferenceOverride Post-processor 裡面提過的,如果使用 Visual Studio 開啟專案時發生找不到 Target Framework:
請到微軟 .Net SDK 網站下載對應的 .Net Framework 安裝,改 Target Framework 硬開所有的路徑會全部丟失。一般 Unity 使用的 .Net 3.5 要安裝.NET Framework 3.5 Runtime,使用 Unity 2017 加入的 4.6 Experimental Scripting Runtime 則是需要安裝 .NET Framework 4.6 Developer Pack(雖然下載連結標記 Visual Studio 2017 有內建 4.6,但實際測試是沒有的,要另外安裝)