[Unity Test]Unityでもテストを書こう・・・書くんだ・・・ウワー!!!テストダイスキーーー!!!
はじめに
この記事はUnityテストを初めて使って間もないです。。。
間違いがあれば指摘をお願いします。。。
ゲームが大きくなってくると、一つの機能を確認するためにどこどこのシーンに移動して、この関数が実行されるような操作をしないと動作確認できないわけではありません。
Unityにおいてはユニットテスト用の機能があります。Test Runnerと呼ばれるものです。これの使い方が分かるまで時間がかかりました・・・。
入れ方
多分たいてい入っています。
アプリウインドウ上部の(ファイルとか編集とかあるとこ)ウインドウをクリック→パッケージマネージャーをクリック→右上の検索窓からTestを入力
これです。自分はアップデート可能だったので、アップデートしました。
テストするスクリプトの準備(EditMode)
まずはテスト用のスクリプトを書きます。これはstatic classのテストです。Unityでなくてもいいよねって部分のテストですね。後でインスタンスを作成するようなテストも書きます。
このようなUnityでなくてもいいよねって部分はEditModeでテストします。(後でまた出てきます)
他のものの使いまわしですが、こんなものをテストします。
(テスト用の関数をテストしています・・・)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public static class Utils
{
public static bool ComparePiecePoint<T>(T[] Ex, T[] Act) where T : IComparable
{
if (Ex[0].Equals(Act[0]))
{
if (Ex[1].Equals(Act[1]))
return true;
}
return false;
}
}
ここで簡単に関数について説明します。
基本機能:
長さ2の配列を2つ受け取り、その二2つが同じかどうかを確認し、同じであればtrue、異なればfalseを返します。
関数名の最後の <T> :
ジェネリック型を扱うことを表しています。これだけでどんな型の配列でも受け取れます。(引数がint,intでもfloat,floatでも同じ関数が動く)
引数の後ろの where T : IComparable :
Tはどんな型でも受け取れますが、基本機能的に必ず比較できる必要があります。そこでIComparableのインターフェースを持っている型は必ずEquals関数を持っており、その関数で比較が可能であることを利用します。
このwhereの部分でTに来る型はIComparableを実装してくださいって感じのことを言っています。
ちなみにT型同士を==とか>とかで比較は簡単にはできません。
Ex[0].Equals(Act[0]) :
Ex[0]とAct[0]が等しければtrue、異なればfalseを返します。
配列同士を比較する場合、配列のポインタの比較になるようなので、配列内の要素が同じであるかをひとつづつ確認する必要があります。
テストするスクリプトを独自のアセンブリにいれる
言葉が間違っているかもしれませんが、こんなイメージです。
アセンブリはスクリプトファイルをコンパイル(機械側が解釈できる感じに整える)した後ってイメージです。複数のアセンブリを組み合わせることで、一つのアセンブリのようにも扱えます。
(自分はそんなイメージ)
Unity内で普通にスクリプトを置いた場合、Assembly-CSharp.dllというアセンブリに入れられます。このアセンブリに入っている関数はのちのち出てくるテスト用のアセンブリに指定できません。独自のアセンブリに入れる必要があります。
スクリプトを保存したフォルダー内で
右クリック→作成→アセンブリ定義 の順でクリックします。
パズルのピースっぽいアイコンができたと思います。ここでは名前をUtilsとしておきます。
このファイル以下の階層にあるスクリプトはこのアセンブリに入れられます。このアセンブリ以上の階層にアセンブリ定義ファイルは入れられません。
テスト用スクリプトを準備
テスト用のフォルダーをAsset直下に作っていますが、基本どこでもいいです。
テスト用フォルダー内で
右クリック→作成→Testing→Tests Assembly Folder を選択
フォルダー名をEditorにしておきましょう。
Editorというフォルダー以下のスクリプトしかテストできません。
また、複数のEditorというフォルダーがあってもかまいません。
Editor配下にEditorフォルダーがあるのはダメだと思います。
これで作成したフォルダー内にテストスクリプトを書いていきます。
すでにアセンブリ定義ファイルがありますが、一旦無視してください。
UtilsTestみたいな名前のスクリプトを作成します。
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public class UtilsTest
{
[TestCase(new int[]{1, 3}, new int[]{1, 3}, true)]
[TestCase(new int[]{1, 3}, new int[]{3, 1}, false)]
public void UtilsTestComparePoint(int[] Ex, int[] Act, bool ExBool)
{
Assert.AreEqual(ExBool, Utils.ComparePiecePoint(Ex, Act));
}
}
ここでテスト用の関数の前には[TestCase]とかを付けることでテストとして認識されます。ほかの書き方としては
[Test]
public void UtilsTestComparePoint1()
{
Assert.AreEqual(true,
Utils.ComparePiecePoint(new int[]{1, 3}, new int[]{1, 3}));
}
[Test]
public void UtilsTestComparePoint2()
{
Assert.AreEqual(false,
Utils.ComparePiecePoint(new int[]{1, 3}, new int[]{3, 1}));
}
static object[] TestCase =
new object[] {
new object[]
{
new int[]{1, 3},
new int[]{1, 3},
true
},
new object[]
{
new int[]{1, 3},
new int[]{3, 1},
false
},
};
[TestCaseSource(nameof(TestCase))]
public void UtilsTestComparePoint3(int[] Ex, int[] Act, bool ExBool)
{
Assert.AreEqual(ExBool, Utils.ComparePiecePoint(Ex, Act));
}
があります。
TestCaseSourceの方は行数が多くなりますが、複雑な引数を渡す場合は見やすくなると思います。
TestCaseの方はシンプルですが、複雑な引数は少し見づらくなりそうです。
Testの方は使いづらいですね。テストが1回のみ以外はおすすめしません。
テストフォルダー内のアセンブリ定義ファイルの設定を行う
先ほどテスト用のフォルダー内にアセンブリ定義ファイルが作成されているはずです。
その設定を行います。
設定する点は2つです。アセンブリ定義参照部分にUtilsを追加してください。これがないとUtilsのスクリプト内の関数を参照できません。
また、ここでAssembly-CSharp.dllを指定できないためUtils用のアセンブリ定義ファイルを作成する必要がありました。
プラットフォームはEditorのみにチェックしてください。
任意のプラットフォームのチェックを外す→下の方のすべての選択を解除→Editorをクリック
で可能です。
テストを実行する
アプリウインドウ上部のウインドウ→一般→Test Runnerを選択
テスト実行用のウインドウが出てきます
こんなやつです。
上部にPlayModeとEditModeがありますが、今回はEditModeです。
PlayModeはシーンを作成するような場合に使用するみたいです。
左上のRun Allを実行すると、画像のような結果になります。
Debug.Logで表示されるログはテストごとに分けられるので複数のテストでも経過が分かりやすいと思います。
シーンに配置するようなテストを実行する(PlayMode)
PlayModeでのテストです。まだあんまり使えてないのでざっくりとした説明です。
EditModeに比べ実行まで時間が多少かかります(1秒とか)。できるだけEditModeでテストを行う方が良いと思います。
実際に作ったテスト。
Pieceというスクリプトがあるオブジェクト(プレハブ)を作成し、その初期設定がうまくいっているかを確認します。
static object[] TestCaseCheckPieceDesign_PieceList =
new object[] {
new object[]
{
true,
"P3-2",
new List<int[]>{new int[]{0, 0}, new int[]{1, 0}, new int[]{2, 0}},
},
new object[]
{
false,
"P3-2",
new List<int[]>{new int[]{0, 0}, new int[]{1, 0}, new int[]{3, 0}},
},
new object[]
{
true,
"P4-3",
new List<int[]>{new int[]{0, 0}, new int[]{1, 0}, new int[]{1, 1}, new int[]{0, 1}},
},
new object[]
{
true,
"P5-6",
new List<int[]>{new int[]{0, 0}, new int[]{1, 0}, new int[]{1, 1}, new int[]{1, -1}, new int[]{2, 1}},
},
};
[UnityTest]
public IEnumerator PieceDesignPTestCheckPieceDesign_PieceList()
{
foreach (object[] TestCase in TestCaseCheckPieceDesign_PieceList)
{
Debug.Log("TestCase" + (string)TestCase[1]);
GameObject NewPiece = Object.Instantiate((GameObject)Resources.Load((string)TestCase[1]));
NewPiece.transform.position = new Vector3(0f, 0.3f, 0f);
yield return null;
Piece Script = NewPiece.GetComponent<Piece>();
Assert.AreEqual((bool)TestCase[0], Utils.ComparePiecePointList((List<int[]>)TestCase[2], Script.Design));
yield return null;
}
}
TestCaseみたいにテーブルを使用することはできないみたい?
[Test]ではなく[UnityTest]を使います。また、テストの関数の返り値もIEnumeratorになります。
yield return null;
ではシーンが1コマ進みます。
Instantiateでは関数が見つからなかったのでObject.Instantiateにしています。
シーン内の時間の進みも早くしても良さそうですね。
テスト用のアセンブリ定義ファイルはプラットフォームの部分を任意のプラットフォームにしてください。
アセンブリ定義参照はUnityEditor.TestRunnerは削除しておいてください。
テストの実行はTest Runner上部からPlay Modeを選択してRunAllを実行することで可能です。
最後に
テスト作るのってそれだけで面倒ですよね。そもそも新しいフレームワーク導入でも面倒ですよね。ウワーーー!!!ってなります。これをみて少しでも簡単にテストが作れればと思います。
この記事が気に入ったらサポートをしてみませんか?