Unity で ユニットテストをする
Unity 5.3 から、ユニットテストがエディタ組み込みのツールで実行できるようになった。 簡単なユニットテストを定義し、CI(Jenkins)で実行してみる。
概要
- Editor Test Runner を使ってユニットテストを実行してみる
- Unity Editor の GUI からテストを実行してみる
- Jenkins でテストを実行し、テスト結果を表示してみる
環境
- Unity 5.3.2
基本的なこと
Unity - Manual: Editor Test Runner を読む。
Unity Editor で実行する
まずは、GUI(Unity Editor)から実行してみる。
ユニットテストを定義する
Assets > Create > Editor Test C# Script で作成する。テストスクリプトは、Editor
フォルダ内に配置する必要がある。
折角 Unity でユニットテストするので、MonoBehaviour
を継承したクラスをユニットテストしてみる。
Assets/Scripts/Monster.cs
using UnityEngine;
using System.Collections;
public class Monster : MonoBehaviour
{
}
Assets/Scripts/MonsterSpawner.cs
using UnityEngine;
using System.Collections;
public class MonsterSpawner : MonoBehaviour
{
public Monster Spawn()
{
var monster = new GameObject("monster", typeof(Monster)).GetComponent<Monster>();
monster.transform.position = this.transform.position;
return monster;
}
}
Assets/Tests/UnitTests/Editor/MonsterSpawnerTests.cs
using UnityEngine;
using UnityEditor;
using NUnit.Framework;
public class MonsterSpawnerTests
{
[Test]
public void SpawnCreatesMonsterAtPositionOfSpawner()
{
var spawner = new GameObject("spawner", typeof(MonsterSpawner)).GetComponent<MonsterSpawner>();
spawner.transform.position = new Vector3(1.0f, 0.0f, -0.1f);
var monster = spawner.Spawn();
Assert.That(monster.transform.position, Is.EqualTo(spawner.transform.position));
}
}
MonoBehaviour でテストするもの
GameObject のプロパティをテストしたら良いと思う。サンプルでは、transform をテストしてみた。
Monster の状態(例えば HP など)は、Monster のモデルを作って管理すると思うので、モデルでテストするべきだと思う。
テストの名前
NUnit の例を nunit-csharp-samples/MoneyTest.cs at master · nunit/nunit-csharp-samples · GitHub などで見てみると、 テスト対象のメソッド名がそのままテストの名前になっている。しかし、これではテストの名前を見ても、何がテストされているのか分からない。
Effective Unit Testing with NUnit でも指摘されているとおり、 テストの名前は descriptive であるべきだと思う。
今回の例で言うと、テストを SpawnTest
のような名前にすると、このテストが Fail しても、「Spawn メソッドの何かが壊れている」までしか分からない。
代わりに SpawnCreatesMonsterAtPositionOfSpawner
という名前にすれば、「Spawn メソッドが Monster の位置の初期化に失敗しているっぽい」というところまで分かる。
実行する
Window > Editor Test Runner でユニットテスト一覧が開く。
ウィンドウ左上の、Run XXX からテストを実行できる。
より詳しくは、Unity - Manual: Editor Test Runner の “Editor Tests Runner window overview” の項を参照する。
Jenkins で実行する
せっかくユニットテストをするので、CI でテストを実行したい。 Test Runner が出力してくれるテスト結果も表示する。なお、カバレッジは取らない。
CLI から実行する
-runEditorTests
オプションを付けて Unity を起動すると、CLI からユニットテストを実行できる。
より詳しくは、Unity - Manual: Editor Test Runner の “Headless runs (batch mode)” の項を参照する。
シェルの実行
Jenkins ジョブの Build > Execute shell でユニットテストを実行する。 ただし、ジョブに直接スクリプトを書き込むのではなく、スクリプトはファイルにして、ジョブではファイルを実行するだけにすると、 開発環境のローカルマシンでもテストを実行できたり、スクリプトを Git 管理できたりして捗る。
test_unit.sh
if [ -z "${WORKSPACE}" ]; then WORKSPACE=$(PWD); fi
/Applications/Unity/Unity.app/Contents/MacOS/Unity -projectPath $WORKSPACE -quit -batchmode -runEditorTests
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
cat $HOME/Library/Logs/Unity/Editor.log
exit $EXIT_CODE
fi
ファイルは、プロジェクトルートに配置することを想定している。また、OSX で実行することを想定している。
テスト結果を表示する
Editor Test Runner は NUnit スタイルの xml ファイルでテスト結果を出力してくれる。
NUnit Plugin - Jenkins - Jenkins Wiki を導入して、
Post-build Actions > Publish NUnit test result report で EditorTestResults.xml
を読ませれば良い。
カバレッジについて
カバレッジを計測する方法は調査中。 ただし、インディーゲームを作るにあたって、カバレッジまで計測する必要があるのかについては疑問の余地があり、 調査のモチベーションは低い。
まとめ・感想
- Editor Test Runner を使ってユニットテストを実行した
- MonoBehaviour のサブクラスをユニットテストした
- ユニットテストを CLI から実行する手順をスクリプトにまとめた
- Jenkins でユニットテストを実行し、テスト結果を表示した
エディタ組み込みのツールでユニットテストを実行できるようになったので、ユニットテストがより簡単にできるようになったのが良い。
ただし、Coroutine や非同期な API のテストには対応していない。 これらのテストは、Unity Test Tools の Integration Test Frameworks を使って実行することができる。
インディーゲーム開発者としては、
- わざわざゲームを起動して UI からテストするのがだるいとき
- クラスの API 設計に不安があり、TDD っぽいことしたいとき
にテストを書くと、幸せになれそうな気がする。