単体テストとBDDに関する多くの情報があります。この記事では、UnitFlowで動作するようにSpecFlowを構成することに焦点を当て、コンポーネント指向のアプローチでテスト可能なゲームアーキテクチャを作成する一般的な推奨事項を提供します。
次のように、簡単にテストできるコンポーネントを開発するための一般的な規則を策定しました。
1)人だけが見る/聞くことができるものは、別のコンポーネントになければなりません。
2)コンポーネントにはデフォルトの動作が必要です。 参照と見なすことをお勧めします。
3)忘れないでください:1つのコンポーネント-1つの動作。
そのため、タワーディフェンスのコアゲームプレイの実装を調整しました。
一部のコンポーネントは、GetComponent()を介してRendererにアクセスします。これは、Rendererコンポーネントの必須の存在に対する不必要な依存関係です。 すべての視覚化は個別のコンポーネントで行う必要があります。 すべてのフィールドに指定されたデフォルト値:これにより、コンポーネント自体の構成が簡素化されます。 また、プレーヤーを導入することにより、ゲームの機能を拡張しました。
プレイヤーとは、入力デバイスを介してゲームに制御を移すことを行動とする人物です。
ここで、ゲームを開始するには、UIの[ゲームの開始]ボタンをクリックし、UIで構築するタワーを選択して、どこに構築するかを示す必要があります。 そして、すべてのタワーが構築されると、ゲームが始まります。
しかし、問題は、どのコンポーネントに対して単体テストが必要であり、どのコンポーネントが仕様に従って最もよくチェックされるのかということです。
この質問に対する答えは、コンポーネントの数とプロジェクトの複雑さによって異なります。 ゲームオブジェクトの全体的な動作は、コンポーネントの動作で構成されていることを覚えておく価値があります。 そして、私の推薦はこれです:
コンポーネント内にロジックを実装する複雑さを評価する必要があります。 間違いを犯しやすい場合は、安全で、コンポーネントの個別の単体テストを行う必要があります。 それ以外の場合は、ゲームオブジェクト全体の動作を確認する一般的な仕様を作成できます。
準備作業。
Unityへの追加から、次のものが必要になります。
1) UnityTestTools-エディター環境のUnity開発者によるNUnitテストのランナー。
2) Visual Studio Tools (以前のUnityVS)-デバッグ用のプラグイン。
Visual Studioの場合は、 SpecFlowが必要です
そして、ユニットテストから始めましょう。
多くの場合、単体テストをプロジェクト自体に直接追加しますが、アセンブリに負担をかけないでください。 テストスイートを個別のアセンブリに分割するのにそれほど時間はかかりません。このアセンブリは、ゲームの最終バージョン(ビルド)には含まれません。
UnityTestToolsをUnityに追加した後、ソリューション(UnityVsによって生成される)を開き、「クラスライブラリ」などの新しいプロジェクトを追加する必要があります。これにはコンポーネントのユニットテストが含まれます。 たとえば、TowerDefenseCore.UnitTestsというアセンブリを呼び出しましょう。 重要なポイントは、アセンブリを構成することです。
1)アセンブリ参照で、次を追加します。
-Assets \ UnityTestTools \ UnitTesting \ Editor \ NUnit \ Libsからの「nunit.framework.dll」および「nunit.core.dll」
-Library \ UnityAssembliesからの「UnityEngine.dll」
-ソリューションのアセンブリ「Assembly-CSharp.dll」
重要:それらをローカルにコピーしないでください。
2)出力パスを指定して、アセンブリプロパティを構成する必要があります。 アセンブリはAssetsディレクトリにある必要があります-たとえば、Assets \ Tests。
Unity Test Runnerで実行される単体テストの作成は、通常の単体テストと変わりません。 ただし、いくつかの重要なポイントがあります。
最初:ゲームオブジェクトをインスタンス化しない(GameObject.Instantiateメソッド)。
これは、DamageApplicatorコンポーネントの簡単なテストです。
[Test] public void DamageApplicator_DirectDamage() { var damageApplicator = new DamageApplicator(); var hp = damageApplicator.DirectDamage(100, 10); Assert.AreEqual(90f, hp); }
DamageApplicatorは、Awake / Start / OnEnableメソッドで解決される他のコンポーネントに依存しません。 ただし、コンポーネントがこの目的でAwakeを使用する場合は、強制的に呼び出す必要があります。
[Test] public void Hittable_DeadOnDirectDamage() { var componentsHolder = new GameObject(); // , componentsHolder.AddComponent<DamageAplicator>(); var hittable = componentsHolder.AddComponent<Hittable>(); hittable.Awake();//: , DamageApplicator hittable.DirectDamage(50); Assert.AreEqual(50, hittable.HP); hittable.DirectDamage(50); Assert.AreEqual(true, hittable.IsDead); }
2番目:コルーチンを使用してコンポーネントの動作を確認します。 ここでは、MaxTime、計算された参照ランタイム(存在する場合)、またはwhileループでテストが合格したかどうかを確認するための追加チェックを示すことが重要です。
[Test] [MaxTime(10000)]//BeginDPS 5 0.5 , 100HP public void DamageInflictor_BeginDPS() { var target = new GameObject(); target.AddComponent<DamageApplicator>(); var hittable = target.AddComponent<Hittable>(); hittable.Awake(); var tower = new GameObject(); var dmger = tower.AddComponent<DamageInflictor>(); dmger.BeginDPS(hittable);// Coroutine while (dmger.inflictDamage().MoveNext())// Coroutine Thread.Sleep(100); Assert.True(hittable.IsDead); }
3番目のポイント:シーン全体ではなく、各コンポーネントの動作をテストすることをお勧めします。 オプションのテストランナー設定で、[新しいシーンでテストを実行]を指定します。
アセンブリを組み立て、Unityに切り替え、Unity Test ToolsメニューからTest Runnerを選択します。 ご覧のとおり、アセンブリのテストが実行されているように見えます。
SpecFlowの設定。
ソリューションにTowerDefenseCore.Specsクラスライブラリを作成します。 設定してください:
1)パッケージを追加します:Install-Package SpecFlow.NUnit
2)ビルドの依存関係を追加します。
-\ packages \ SpecFlow.1.9.0 \ lib \ net35からの「TechTalk.SpecFlow.dll」
-Library \ UnityAssembliesからの「UnityEngine.dll」
-ソリューションのアセンブリ「Assembly-CSharp.dll」
重要:それらをローカルにコピーしないでください。
3)出力パスを指定して、アセンブリプロパティを構成する必要があります。 アセンブリはAssetsディレクトリにある必要があります-たとえば、Assets \ Tests。
4) 重要: 「TechTalk.SpecFlow.dll」をAssets \ UnityTestTools \ UnitTesting \ Editor \ NUnit \ Libsにコピーします。
テストランナーには、TowerDefenseCore.UnitTestsからのテストに加えて、TowerDefenseCore.Specsからのステップがあります。
損傷を受けたときにクリープ動作をチェックする簡単な機能の例:
機能:CreepLogic
クリープアライブ、ダメージを受けて死にます。
@クリープ
シナリオ:チェッククリープが停止している
与えられたクリープは生きている
クリープがダメージを100にしたとき
その後、クリープは死んでいます
そして生成されたステップ:
[Binding] public class CreepLogicSteps { private Hittable _creep; [Given(@"Creep is alive")] public void GivenCreepIsAlive() { var componentsHolder = new GameObject(); componentsHolder.AddComponent<DamageApplicator>(); _creep = componentsHolder.AddComponent<Hittable>(); _creep.Awake(); } [When(@"Creep take damage (.*)")] public void WhenCreepTakeDamage(int dmg) { _creep.DirectDamage(dmg); } [Then(@"Creep is dead")] public void ThenCreepIsDead() { NUnitFramework.Assert.AreEqual(true, _creep.IsDead); } }
テストランナーでは、テストはCheckCreepIsDeadという名前で表示されます。

記事の冒頭で、プレーヤーの動作について説明しました。 人間の介入なしで彼の行動の検証を自動化する方法は何ですか-別の記事のトピック。