Unityのテスト関連機能、使っていますでしょうか?僕は使ったことがありません。。
個人開発ではなかなか使わない機能の一つですが、テストの重要性は言うまでもないので、なんとか調べながら使ってみたいと思います。
テストの概要や使いみちなどはこちらのUniteの動画がわかりやすいです。
テストには二種類あり、EditModeTestはクラス自体の単体テスト、PlayModeTestは結合テストとなるとのことです。
EditModeTestでは、Monobehaviourを継承したクラスのテストは難しいので、シーン上に配置するオブジェクトなどのテストを行うには、PlayModeTestを使用するようです。
さて、安定版のUnity2018.4を起動して、メニューの「Window > General > Test Runnder」を選択すると、Test Runnderウインドウが出てきます。タブの「PlayMode」「EditMode」でテストの切り替えができます。「PlayMode」をクリックしてみましょう。
Unityの以前のバージョンでは、どこでもテストスクリプトを作れたらしいのですが、2018.4では、「Create PlayMode Test Assembly Folder」ボタンしか有効になっていません。このボタンをクリックすると、テストスクリプト用のフォルダが作られるのですが、今回は押さないでおきましょう。
クリックしてフォルダを作成した場合、Assembly Definitionという機能により、テストコード専用のDLLが生成されることとなります。これは、リリース版のビルドを作成する際に本体とテストコードのDLLが分離可能となり好ましいのですが、今回は入門なのでそこまでは考慮しません。DLLが分離してしまうと、自作のクラスを動作させるために参照設定が必要になり煩雑なためです。
※きちんとAssembly Definitionを使いながらテストを行うにはこちらが参考になります
ウインドウの右上にある三本線のメニューをクリックすると、「Enable playmode tests for all assemblies」という項目が出てきます。これを選択すると、「You need to restar the editor now」というダイアログが出ますので、いったんUnityを終了して再度起動しましょう。
すると、めでたく「Create Test Script in current folder」ボタンが有効になります。
さて、そのボタンでスクリプトを作成すると、下記のようなテンプレートとなっています。
using System.Collections; using System.Collections.Generic; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; namespace Tests { public class MyTest { // A Test behaves as an ordinary method [Test] public void MyTestSimplePasses() { // Use the Assert class to test conditions } // A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use // `yield return null;` to skip a frame. [UnityTest] public IEnumerator MyTestWithEnumeratorPasses() { // Use the Assert class to test conditions. // Use yield to skip a frame. yield return null; } } }
usingされているNUnitというのが、テストを行うためのフレームワークになります。
NUnitに指示を出すには、[Test]のようなアトリビュートを関数の上に書きます。
さて、記載するテストの内容ですが、初めてテストを書くのでやり方がぜんぜん違うのかもしれませんが、下記の方法でやってみました。
まず、テスト用のシーンを新規作成します。
その中で下記の操作をします。
・Planeを作成してPositionを(0,0,0)にします。
・Cubeを作成し、Positionを(0,2,0)にします(Planeの上空に配置)
・CubeにRigidbodyをアタッチし、Use Gravityにチェックが入っていることを確認します
これでRunすると、Cubeは重力のため落ちて、Planeの上で止まるはずです。
テストしたいのは、Cubeにちゃんと重力が働いているかどうかです。
このシーンだけ見ていればそんなの当たり前のことなのですが、例えばUse Gravityのチェックボックスを誤操作で間違ってoffにしてしまったり、Cubeに別のコンポーネントをつけることによって重力が無視されてしまったり、重力の値そのものが別の場所で変更されて0になってしまったりと、様々な変更によって挙動が変わってしまう、ということが起こりえます。そのことを機械的に何度でもチェックできるため、気兼ねなく機能追加やリファクタリングができる、というのがテストを書く利点となります。
作成したシーンは保存して、Build SettingsでAdd Open Scenesを使ってScenes In Buildに追加しておきましょう。
そして修正したテストコードが下記となります。
using System.Collections; using System.Collections.Generic; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; using UnityEngine.SceneManagement; namespace Tests { public class MyTest { GameObject cube; [SetUp] public void Init() { SceneManager.LoadScene("SampleScene"); } [UnityTest] public IEnumerator FallTest() { if (cube == null) cube = GameObject.Find("Cube"); yield return new WaitForSeconds(2f); Assert.That(Mathf.Abs(cube.transform.position.y - 0.5f) < Mathf.Epsilon, "OK!"); } } }
[SetUp]というアトリビュートは、テストの実行前に自動的に実行されるメソッドを指示します。作成したテスト用シーンをロードしています。
そして[UnityTest]によってテストを行います。(今回は非同期のため[Test]は使用できません)。
[UnityTest]属性を付けたFallTest()関数では、シーン内からCubeを見つけて変数に格納し、2秒待ってから、Assertで条件をチェックしています。
Cubeは重力で落ちると、PositionのYが0.5になるはずです(床が0、高さが1なので)
floatは誤差が出るので、誤差が極小値ならばテストがOKとなる、としています。
スクリプトに問題がなければ、Test Runnerに、作成したテストメソッドが表示されます。
[UnityTest]属性を付けたFallTestが見えます。
そして、メソッド名をダブルクリックすると、テストが実行されます。
緑のチェックマークがついて、テストが通りました!!
ためしにUse Gravityをoffにしてシーンを保存して再実行すると、テストは失敗の結果となります。予想通りの動きとなっているようです。
ということで、見よう見まねですがテストを使ってみました。
今後の開発でも使っていきたいと思います!