Unity Test Frameworkとは?
Unity Test Frameworkは、Unityに標準搭載されているテストフレームワークです。
NUnitをベースとしており、C#のユニットテストを作成・実行できます。コードの品質を保ち、リファクタリングやバグ修正を安全に行うために、テストは非常に重要な役割を果たします。
Unity Test Frameworkには、EditModeテストとPlayModeテストの2種類があり、それぞれ異なる目的で使用します。
EditModeテストとは?
EditModeテストは、Unity Editorの環境で実行されるユニットテストです。
ゲームの実行を伴わず、エディタ上で即座にテストが完了します。
計算ロジック、ユーティリティクラス、データクラスなど、GameObjectやMonoBehaviourのライフサイクルに依存しないコードのテストに最適です。
PlayModeテストとの違い
| 特徴 | EditMode | PlayMode |
|---|---|---|
| 実行環境 | エディタ環境 | ゲーム実行環境 |
| 実行速度 | 高速 | 低速 |
| GameObject使用 | 制限あり | 可能 |
| 用途 | ロジックのテスト | ゲーム動作のテスト |
EditModeテストは高速に実行できるため、開発中に頻繁に実行して、コードの変更が既存機能を壊していないか確認できます。
導入方法
Test Frameworkパッケージのインストール
Unity Test Frameworkは、Unity 2019.2以降では標準でインストールされています。
もしインストールされていない場合は、Package Managerから追加できます。
Window→Package Managerを開くUnity RegistryからTest Frameworkを検索- インストール

Test Runnerウィンドウの開き方
テストを実行・管理するには、Test Runnerウィンドウを使用します。
Window → General → Test Runnerでウィンドウを開きます。

テスト用フォルダの作成
テストコードは専用のフォルダに配置します。
Projectウィンドウで右クリックCreate→Testing→Tests Assembly Folderを選択- フォルダ名を
Testsなどに設定
この操作により、テスト専用のAssembly Definitionを持つフォルダが作成されます。

基本的なテストの書き方
最もシンプルなテストの例を見てみましょう。
シンプルな計算テスト
using NUnit.Framework;
public class SimpleCalculatorTest
{
[Test]
public void Add_TwoPlusThree_ReturnsFive()
{
// Arrange(準備)
int a = 2;
int b = 3;
// Act(実行)
int result = a + b;
// Assert(検証)
Assert.AreEqual(5, result);
}
[Test]
public void Multiply_TwoTimesThree_ReturnsSix()
{
// Arrange
int a = 2;
int b = 3;
// Act
int result = a * b;
// Assert
Assert.AreEqual(6, result);
}
}
テストの構造:
[Test]属性: メソッドがテストであることを示す- Arrange(準備): テストに必要なデータを準備
- Act(実行): テスト対象のコードを実行
- Assert(検証): 結果が期待通りか検証
テスト命名規則
テストメソッド名は、以下の形式が推奨されます:
メソッド名_テスト条件_期待される結果
例:
Add_TwoPlusThree_ReturnsFiveGetDamage_WithArmor_ReducesDamageIsValid_WithNullParameter_ReturnsFalse
EditModeテストの実装例
計算ロジックのテスト
ダメージ計算のユーティリティクラスをテストします。
テスト対象のクラス:
public static class DamageCalculator
{
public static int CalculateDamage(int baseDamage, int defense)
{
if (baseDamage < 0 || defense < 0)
{
throw new System.ArgumentException("ダメージと防御力は0以上である必要があります");
}
int damage = baseDamage - defense;
return damage > 0 ? damage : 0;
}
public static float CalculateCriticalDamage(int baseDamage, float criticalMultiplier)
{
return baseDamage * criticalMultiplier;
}
}
テストコード:
using NUnit.Framework;
using System;
public class DamageCalculatorTest
{
[Test]
public void CalculateDamage_WithNormalValues_ReturnsCorrectDamage()
{
// Arrange
int baseDamage = 100;
int defense = 30;
// Act
int result = DamageCalculator.CalculateDamage(baseDamage, defense);
// Assert
Assert.AreEqual(70, result);
}
[Test]
public void CalculateDamage_WhenDefenseExceedsDamage_ReturnsZero()
{
// Arrange
int baseDamage = 50;
int defense = 100;
// Act
int result = DamageCalculator.CalculateDamage(baseDamage, defense);
// Assert
Assert.AreEqual(0, result);
}
[Test]
public void CalculateDamage_WithNegativeBaseDamage_ThrowsException()
{
// Arrange
int baseDamage = -10;
int defense = 30;
// Act & Assert
Assert.Throws<ArgumentException>(() =>
{
DamageCalculator.CalculateDamage(baseDamage, defense);
});
}
[Test]
public void CalculateCriticalDamage_WithDoubleMultiplier_ReturnsDoubledDamage()
{
// Arrange
int baseDamage = 100;
float criticalMultiplier = 2.0f;
// Act
float result = DamageCalculator.CalculateCriticalDamage(baseDamage, criticalMultiplier);
// Assert
Assert.AreEqual(200f, result);
}
}
データクラスのテスト
ScriptableObjectを使ったデータクラスのテストです。
テスト対象のクラス:
using UnityEngine;
[CreateAssetMenu(fileName = "EnemyData", menuName = "Game/Enemy Data")]
public class EnemyData : ScriptableObject
{
public string enemyName;
public int maxHp;
public int attackPower;
public bool IsValid()
{
return !string.IsNullOrEmpty(enemyName) && maxHp > 0 && attackPower >= 0;
}
public int GetDamageWithMultiplier(float multiplier)
{
return Mathf.RoundToInt(attackPower * multiplier);
}
}
テストコード:
using NUnit.Framework;
using UnityEngine;
public class EnemyDataTest
{
private EnemyData _enemyData;
[SetUp]
public void SetUp()
{
// 各テストの実行前に呼ばれる
_enemyData = ScriptableObject.CreateInstance<EnemyData>();
}
[TearDown]
public void TearDown()
{
// 各テストの実行後に呼ばれる
Object.DestroyImmediate(_enemyData);
}
[Test]
public void IsValid_WithValidData_ReturnsTrue()
{
// Arrange
_enemyData.enemyName = "Goblin";
_enemyData.maxHp = 100;
_enemyData.attackPower = 20;
// Act
bool result = _enemyData.IsValid();
// Assert
Assert.IsTrue(result);
}
[Test]
public void IsValid_WithEmptyName_ReturnsFalse()
{
// Arrange
_enemyData.enemyName = "";
_enemyData.maxHp = 100;
_enemyData.attackPower = 20;
// Act
bool result = _enemyData.IsValid();
// Assert
Assert.IsFalse(result);
}
[Test]
public void IsValid_WithZeroHp_ReturnsFalse()
{
// Arrange
_enemyData.enemyName = "Goblin";
_enemyData.maxHp = 0;
_enemyData.attackPower = 20;
// Act
bool result = _enemyData.IsValid();
// Assert
Assert.IsFalse(result);
}
[Test]
public void GetDamageWithMultiplier_WithTwoTimesMultiplier_ReturnsDoubledDamage()
{
// Arrange
_enemyData.attackPower = 25;
float multiplier = 2.0f;
// Act
int result = _enemyData.GetDamageWithMultiplier(multiplier);
// Assert
Assert.AreEqual(50, result);
}
}
文字列処理のテスト
ユーティリティクラスの文字列処理をテストします。
テスト対象のクラス:
public static class StringHelper
{
public static string Truncate(string text, int maxLength)
{
if (string.IsNullOrEmpty(text))
{
return text;
}
if (text.Length <= maxLength)
{
return text;
}
return text.Substring(0, maxLength) + "...";
}
public static string RemoveWhiteSpace(string text)
{
return text?.Replace(" ", "").Replace("\\t", "").Replace("\\n", "");
}
}
テストコード:
using NUnit.Framework;
public class StringHelperTest
{
[Test]
public void Truncate_WithShortText_ReturnsOriginalText()
{
// Arrange
string text = "Hello";
int maxLength = 10;
// Act
string result = StringHelper.Truncate(text, maxLength);
// Assert
Assert.AreEqual("Hello", result);
}
[Test]
public void Truncate_WithLongText_ReturnsTruncatedText()
{
// Arrange
string text = "This is a very long text";
int maxLength = 10;
// Act
string result = StringHelper.Truncate(text, maxLength);
// Assert
Assert.AreEqual("This is a ...", result);
}
[Test]
public void Truncate_WithNullText_ReturnsNull()
{
// Arrange
string text = null;
int maxLength = 10;
// Act
string result = StringHelper.Truncate(text, maxLength);
// Assert
Assert.IsNull(result);
}
[Test]
public void RemoveWhiteSpace_WithSpaces_ReturnsTextWithoutSpaces()
{
// Arrange
string text = "Hello World Test";
// Act
string result = StringHelper.RemoveWhiteSpace(text);
// Assert
Assert.AreEqual("HelloWorldTest", result);
}
}
よく使うAssertメソッド
Assertクラスには、様々な検証メソッドが用意されています。
等価性の検証
// 値が等しいか
Assert.AreEqual(expected, actual);
// 値が等しくないか
Assert.AreNotEqual(unexpected, actual);
// 浮動小数点の比較(誤差を許容)
Assert.AreEqual(1.0f, 1.001f, 0.01f);
真偽値の検証
// trueか
Assert.IsTrue(condition);
// falseか
Assert.IsFalse(condition);
null検証
// nullか
Assert.IsNull(obj);
// nullではないか
Assert.IsNotNull(obj);
例外の検証
// 特定の例外がスローされるか
Assert.Throws<ArgumentException>(() =>
{
// 例外をスローするコード
});
// 例外がスローされないか
Assert.DoesNotThrow(() =>
{
// 正常に動作するコード
});
コレクションの検証
// コレクションが空か
Assert.IsEmpty(collection);
// コレクションが空ではないか
Assert.IsNotEmpty(collection);
// 要素数の確認
Assert.AreEqual(3, list.Count);
テストの実行方法
Test Runnerでの実行
Test Runnerウィンドウで、EditModeタブを選択し、テストを実行します。

実行方法:
- すべて実行:
Run Allボタンをクリック - 個別実行: テスト名を右クリック →
Runを選択 - 再実行:
Rerun Failedボタンで失敗したテストのみ再実行
テスト結果の確認
- 緑のチェックマーク: テスト成功
- 赤のバツマーク: テスト失敗
- 黄色の警告: テストスキップ
失敗したテストをクリックすると、エラーメッセージとスタックトレースが表示されます。
注意点
MonoBehaviourのテストには制限がある
EditModeテストでは、MonoBehaviourのライフサイクルメソッド(Start, Updateなど)は自動的に呼ばれません。GameObjectを使ったテストにはPlayModeテストを使用してください。
// ❌ EditModeでは動作しない
[Test]
public void TestMonoBehaviour()
{
var go = new GameObject();
var component = go.AddComponent<MyComponent>();
// Start()は呼ばれない
}
テストの独立性を保つ
各テストは互いに独立している必要があります。テストの実行順序に依存しないよう、[SetUp]と[TearDown]を活用しましょう。
private EnemyData _enemyData;
[SetUp]
public void SetUp()
{
// 各テストの前に実行
_enemyData = ScriptableObject.CreateInstance<EnemyData>();
}
[TearDown]
public void TearDown()
{
// 各テストの後に実行
Object.DestroyImmediate(_enemyData);
}
静的変数の使用に注意
静的変数は、テスト間で状態が共有されます。テストの独立性を保つため、静的変数を使用する場合は注意が必要です。
// 静的変数をリセット
[SetUp]
public void SetUp()
{
GameManager.Reset();
}
まとめ
Unity Test FrameworkのEditModeテストは、コードの品質を保つための強力なツールです。
計算ロジック、データクラス、ユーティリティクラスなど、ゲームロジックの中核となる部分をテストすることで、リファクタリングやバグ修正を安全に行えます。テストは高速に実行できるため、開発中に頻繁に実行して、コードの変更が既存機能を壊していないか確認できます。
継続的にテストを書き、実行することで、バグの早期発見と修正が可能になり、開発効率が大幅に向上します。
ぜひプロジェクトにEditModeテストを導入して、品質の高いゲーム開発を実現してください!
