初書 : 2021/06/01
mac : 11.4
unity : 2020.3.10f1
vscode : 1.56.2
前置き
今回はunityでもテストコードを書いておきたい!ということで調べていたらTest Runnerなるものが存在しているらしいので、それを使ってみる。
Test Runnerとは
Unity Test Runner は、Edit (編集) モードと Play (再生) モードでコードをテストするツールです。また、スタンドアロン、Android、iOS などのターゲットプラットフォーム上でも同様にテストできます。
Unity Test Runner - Unity マニュアルより
つまり、Unityに標準でついているテストツール。なるほど、使うしかない。
起動 (?)
果たして起動と表現するものなのか。
とりあえずウィンドウを表示してみる。
ウィンドウ(window)
→一般(general)
→Test Runner
から開くと、上にPlayMode | EditMode
と書かれたウィンドウが開く。
テストは主にこの画面から行うらしい。
テストコードを書く準備をする
テストコードは単純に.cs
を置くだけではできないので、準備をする必要がある。
ディレクトリはどこでもいいのだが、PlayMode
とEditMode
のテストコードと、実際にunityで使うメインのコードはそれぞれ別のディレクトリに入れる必要があるため、ここは簡単に両方のフォルダを作ってしまう。
まず、Assets
直下にTests\Editor
ディレクトリを作成する。
その後、右クリックから作成(create)
→Testing
→Tests Assembly Folder
を選択し、EditModeTests
とPlayModeTests
の2つのディレクトリをそれぞれ作成する。
2つ作成できたら、EditModeTests
の中に入り、EditModeTests.asmdef
1を開く(インスペクターに表示)
その後、プラットフォーム
の箇所をEditor
にのみチェックが入っている状態にする。(任意のプラットフォームのチェックを外し、全ての選択を解除し、Editorにチェック)
これにより、EditModeTests
下に入れたテストスクリプトは、EditMode
で動作し、PlayModeTests
下に入れたスクリプトはPlayMode
で動作する。
PlayMode, EditModeとは
そういえば説明をしていなかった。詳しくどこかに書いているのかは知らないが、
PlayMode : テストコードに沿ってゲームを再生し、テストを行う
EditMode : ゲームの再生を行うことなく、テストコードを実行する
大体2つの違いはこんな感じ。つまり、関数の動作を確認したい時などはEditMode
を使い、わざわざゲームを実行することなくスクリプトをテストできる。
逆に(3D)オブジェクトの動作を確認したい時などはPlayMode
を使い、ゲームを実行しながらテストを行う感じ。
ちなみにこれ記述時点ではEditMode
しか使っていないため、PlayModeの詳しいお話は後日追記する予定。
(EditMode)テストコードを書く…前準備(2回目)
テストコードを書く前に、テスト対象となるコードがなければ話にならないので、まずはそちらを作成する。
Assets/Scripts/TestedCode.cs
を作成し、適当にコードを記述する。
using UnityEngine;
public class TestedCode{
/// <summary>
/// 料金に税金を上乗せします。
/// </summary>
/// <param name="Fee">料金</param>
/// <param name="isFood">食料品かどうか</param>
/// <returns>税金が入った金額</returns>
public static int AddTax(int Fee, bool isFood){
if(isFood){
return MultiplyAndRoundup(Fee, 1.08f);
}else{
return MultiplyAndRoundup(Fee, 1.1f);
}
}
/// <summary>
/// 2数を掛け算し、小数点以下を切り上げ整数にします。
/// </summary>
/// <param name="number">数値</param>
/// <param name="magnification">倍率</param>
/// <returns>切り上げた値</returns>
private static int MultiplyAndRoundup(int number, float magnification){
return Mathf.CeilToInt(number * magnification);
}
}
適当なわりに若干複雑なことしているのは後で使うからです。(そしてこれくらいのコードしか思いつかなかった)
次にテストコードを書きたいのだが、実はこのままだと記述できない。
Assembly Definition
unityにはもう一つ大切なものとして、Assembly Definitionというのがある。これの詳しい説明は他の方に任せるとして、必要な部分だけ説明する。
Unity Assembly Definition 完全に理解した - Qiita
簡単にいうと、複数の.cs
をまとめて一つのアセンブリにする。そのアセンブリを複数に分割するのがAssembly Definition。
で、複数に分割するキーになるものとして、先程のEditModeTests.asmdef
やPlayModeTests.asmdef
が該当する。
このファイルを含んだディレクトリとその下の階層は全て一括りにされて、一つのアセンブリとなる。
・・・説明が下手なので今の階層構造を持ってくると、
├── Scripts
│ ├── TestedCode.cs
└── Tests
└── Editor
├── EditModeTests
│ └── EditModeTests.asmdef
└── PlayModeTests
└── PlayModeTests.asmdef
EditModeTests.asmdef
があるEditModeTests
ディレクトリと、その下(今はない)がEditModeTests.dll
というアセンブリにまとめられ、
PlayModeTests.asmdef
があるPlayModeTests
ディレクトリと、その下(今はない)がPlayModeTests.dll
というアセンブリにまとめられる。
そしてどちらにも入っていないScripts
とその下やTests\Editor
のところは、その他としてAssembly-CSharp
というアセンブリにまとめられる。
で、アセンブリ同士では基本的に変数やクラスのやりとりが出来ず、アクセスを求める際は手動で接続する必要がある。
ただし、Assembly-CSharp
というのは特別で、他のどのアセンブリからも参照することは出来ない。(手動でもできない)
つまり、今からテストするAssets/Scripts/TestedCode.cs
はAssembly-CSharp
にあるため、EditModeTests
アセンブリからアクセスすることが出来ないのである。
(説明難しいな・・・伝わるのかな・・・)
ということでアクセスする方法としては、TestedCode.cs
を含むスクリプトをアセンブリ化するしかないので、それを作成する。
ここまでの説明で理解できて、応用する場合は好きな形でアセンブリを置いてもらえればいいのだが、今回はとりあえずAssets
直下に置くことにする。
右クリック→作成
→アセンブリ定義
で、今回はMainScript
と命名する。
その後、手動で接続する
ために、EditModeTests.asmdef
を開き、アセンブリ定義参照
のところに先程のMainScript
を追加する。
これでようやくテストコードを記述することができる。
テストコードを書く
EditModeTests
ディレクトリにいき、右クリック→作成
→Testing
→C# Test Script
でTestCode
を作成
using NUnit.Framework;
public class TestCode{
[Test]
public void TestCodeAddTax(){
int fee = TestedCode.AddTax(1000,false);
Assert.AreEqual(1100, fee);
}
}
[Test]
と書かれた関数がテスト対象で、正誤判定はAssert.AreEqual
で行っている。
左側に期待値、右側に実測値を入れ、不正解だとエラーが起きる。
これで保存し、Test Runner
の画面を開く。
EditMode
を選択したら、Run All
を選択。
これでテストが行われ、テストが通ればチェックマーク、通らなければ下にエラーが表示される。
これで無事テストが終了した。
private関数のテストコードを書く
TestedCode.csにはもう一つMultiplyAndRoundup
という関数がある。せっかくなのでこちらもテストをしておく。
先ほどと同様にテストをやれればいいのだが、こちらの関数はprivate関数になっていて、そのままではテストできない。
ということで今回はMethodInfo.Invoke
というのを使用して、private関数をテストしてみる。
using System.Reflection; // これだけ先頭に追加。他はTestCodeクラス内
[Test]
public void TestCodeMultiplyAndRoundup(){
System.Type myClass = typeof(TestedCode);
Assert.NotNull(myClass);
MethodInfo myMethod = myClass.GetMethod("MultiplyAndRoundup", BindingFlags.NonPublic | BindingFlags.Static);
Assert.NotNull(myMethod);
var value = (int) myMethod.Invoke(this, new object[]{1000,1.1f});
Assert.AreEqual(1100, value);
}
簡単に解説すると、myClassに型情報を入れ、GetMethod関数を使ってNonPublic
でStatic
なMultiplyAndRoundup
を取り出し、
Invoke
を使って引数に1000,1.1f
を入れて呼び出し、valueに戻り値を入れている。
戻り値はそのまま取得するとオブジェクト型になってしまうので、int型にキャスとしている。
このような形で取得することができる。
あとは先ほどと同様テストするだけ。
終わりに
PlayMode
のテストは後日使ったら追記する。
まだ使いたてなのでもしかしたら間違えているところや、より最適な方法があるかもしれないが、もしそういうのがあればコメントで教えてください。
参考サイト:
Unityでテストを書くのが当然になる時代に今から備えよう - Qiita
Unity のUnitTestでprivate な関数をテストしたい場合 | おなかソフト
その他役立ちそうなサイト:
【Unity】Unity Test Runner(Test Framework)の使い方を総まとめ - インストールから自動化まで - LIGHT11
[C#]private、internalなメソッドのUnitTest - Qiita
-
名前が同じでややこしいので拡張子を記述したが、UnityEditorでは拡張機能を入れない限り拡張子は見えない。 ↩