Unity Test Runner の使い所
目次
1. Edit Mode テストでビジネスロジックを試す
目的: 純粋な C# コード(シーン/ゲームオブジェクト非依存)を高速に検証
- スコア計算
- 例: 
CalculateScore()が期待通りの値を返すか 
 - 例: 
 - インベントリ操作
- 例: アイテム追加/削除メソッドの動作確認
 
 - データ変換・シリアライズ
- JSON ↔ オブジェクトの整合性チェック
 
 
例1: スコア計算機能のテスト(ScoreCalculator)
クラス定義例
public class ScoreCalculator
{
    private readonly int _killPoints;
    private readonly int _timeBonusPerSecond;
    public ScoreCalculator(int killPoints, int timeBonusPerSecond)
    {
        _killPoints = killPoints;
        _timeBonusPerSecond = timeBonusPerSecond;
    }
    /// <summary>
    /// 敵を倒した数とクリア時間から最終スコアを計算する
    /// </summary>
    public int Calculate(int kills, float timeInSeconds)
    {
        int baseScore = kills * _killPoints;
        int timeBonus = Mathf.FloorToInt((60f - timeInSeconds) * _timeBonusPerSecond);
        return Mathf.Max(baseScore + timeBonus, 0);
    }
}
テストクラス例
using NUnit.Framework;
using UnityEngine;  // Mathf を使う場合
[TestFixture]
public class ScoreCalculatorTests
{
    private ScoreCalculator _calculator;
    [SetUp]
    public void SetUp()
    {
        // 1キルあたり100点、1秒早いごとに5点ボーナス
        _calculator = new ScoreCalculator(killPoints: 100, timeBonusPerSecond: 5);
    }
    [Test]
    public void Calculate_GivenZeroKillsAndFullTime_ReturnsZero()
    {
        int result = _calculator.Calculate(kills: 0, timeInSeconds: 60f);
        Assert.That(result, Is.EqualTo(0), "キルもボーナスもない場合は0点になるべき");
    }
    [Test]
    public void Calculate_GivenThreeKillsAndQuickClear_ReturnsCorrectScore()
    {
        // 3キル → 300点、クリア時間50秒 → (60 - 50) * 5 = 50点 → 合計350点
        int result = _calculator.Calculate(kills: 3, timeInSeconds: 50f);
        Assert.That(result, Is.EqualTo(350), "3キルかつ50秒クリアで350点になるべき");
    }
    [Test]
    public void Calculate_NegativeTimeBonus_DoesNotGoBelowZero()
    {
        // クリア時間が60秒以上だと timeBonus がマイナスになるが、最終結果は0未満にならない
        int result = _calculator.Calculate(kills: 0, timeInSeconds: 70f);
        Assert.That(result, Is.EqualTo(0), "マイナスボーナスでもスコアは0未満にならない");
    }
}
例2: インベントリ操作機能のテスト(InventoryManager)
クラス定義例
public class InventoryManager
{
    private readonly List<string> _items = new List<string>();
    public IReadOnlyList<string> Items => _items;
    public void AddItem(string item)
    {
        if (string.IsNullOrEmpty(item))
            throw new ArgumentException("アイテム名は空にできません");
        _items.Add(item);
    }
    public bool RemoveItem(string item)
    {
        return _items.Remove(item);
    }
    public void Clear()
    {
        _items.Clear();
    }
}
テストクラス例
using NUnit.Framework;
using System;
[TestFixture]
public class InventoryManagerTests
{
    private InventoryManager _inventory;
    [SetUp]
    public void SetUp()
    {
        _inventory = new InventoryManager();
    }
    [Test]
    public void AddItem_ValidItem_IncreasesCount()
    {
        _inventory.AddItem("Potion");
        Assert.That(_inventory.Items.Count, Is.EqualTo(1), "アイテム追加後のカウントは1になるべき");
        Assert.That(_inventory.Items[0], Is.EqualTo("Potion"), "追加されたアイテムが正しい");
    }
    [Test]
    public void AddItem_EmptyString_ThrowsArgumentException()
    {
        Assert.That(() => _inventory.AddItem(""), 
                    Throws.ArgumentException.With.Message.Contain("空にできません"));
    }
    [Test]
    public void RemoveItem_ExistingItem_ReturnsTrueAndDecreasesCount()
    {
        _inventory.AddItem("Key");
        bool removed = _inventory.RemoveItem("Key");
        Assert.That(removed, Is.True, "既存アイテムは true を返す");
        Assert.That(_inventory.Items, Does.Not.Contain("Key"), "リストから削除されている");
    }
    [Test]
    public void Clear_OnMultipleItems_EmptiesInventory()
    {
        _inventory.AddItem("Sword");
        _inventory.AddItem("Shield");
        _inventory.Clear();
        Assert.That(_inventory.Items.Count, Is.Zero, "クリア後はアイテム数が0になるべき");
    }
}
これらの Edit Mode テスト例を参考に、
- 小さなロジック(計算処理や単純データ操作)から始め、
 - セットアップ・検証を明確に分けることで、
 - テストの可読性と 保守性 を高められます。
 
2. Play Mode テストでゲーム動作を自動化
目的: 実際のシーンやユーザー操作をエミュレートして動作を確認
- シーンロード検証
SceneManager.LoadScene("Main")→ オブジェクト配置のチェック
 - 操作シミュレーション
- キーボード入力やコライダー衝突の自動テスト
 
 - UI フロー確認
- メニュー開閉やボタン押下後の画面遷移を実行
 
 
例1: シーンロードとプレイヤー生成確認
テスト概要
- シーン名:`MainScene`
 - 検証内容:シーン読み込み後に必ず `"Player"` という名前のゲームオブジェクトがシーン上に存在すること
 
テストコード例
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
public class SceneLoadTests
{
    private const string SceneName = "MainScene";
    [UnityTest]
    public IEnumerator LoadScene_PlayerIsSpawned()
    {
        // シーン読み込みイベントを待機するフラグ
        bool sceneLoaded = false;
        SceneManager.sceneLoaded += (scene, mode) => {
            if (scene.name == SceneName)
                sceneLoaded = true;
        };
        // シーンを非同期読み込み
        var loadOp = SceneManager.LoadSceneAsync(SceneName);
        yield return new WaitUntil(() => sceneLoaded && loadOp.isDone);
        // プレイヤーを検索
        var player = GameObject.Find("Player");
        Assert.IsNotNull(player, "MainScene 読み込み後に Player オブジェクトがシーン上に存在するはず");
        // プレイヤーの初期位置検証(例:ワールド原点付近)
        Vector3 pos = player.transform.position;
        Assert.That(pos, Is.TypeOf<Vector3>(), "Player の位置が Vector3 で取得できること");
        Assert.That(pos.magnitude, Is.LessThan(0.5f), "Player はワールド原点 (0,0,0) 付近にスポーンされるべき");
    }
}
例2: ユーザー入力シミュレーションと移動挙動検証
テスト概要
- プレイヤーに前進入力を送り、PlayerMovement コンポーネントによる移動が正しく行われることを確認
 - Input.GetAxis(“Vertical") をモックして前進させる
 
テストコード例
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
// キーボード入力をプログラムから操作するヘルパークラス
public static class InputSimulator
{
    public static void SetVertical(float value)
    {
        // UnityEngine.InputSystem を使う場合はそちらを利用
        // ここでは Movement スクリプト内で参照している変数を直接書き換える想定
        PlayerInputMock.Vertical = value;
    }
}
// モック入力保持用(テスト用に用意しておく)
public static class PlayerInputMock
{
    public static float Vertical;
}
// プレイヤー移動スクリプト例
public class PlayerMovement : MonoBehaviour
{
    public float speed = 5f;
    void Update()
    {
        float v = PlayerInputMock.Vertical;  // 通常は Input.GetAxis("Vertical")
        transform.Translate(Vector3.forward * v * speed * Time.deltaTime);
    }
}
public class PlayerMovementTests
{
    [UnityTest]
    public IEnumerator PlayerMovesForward_WhenInputPositive()
    {
        // テスト用 GameObject とスクリプトを生成
        var go = new GameObject("Player");
        var movement = go.AddComponent<PlayerMovement>();
        movement.speed = 2f;
        // 初期位置を保存
        Vector3 startPos = go.transform.position;
        // 前進入力をシミュレート
        InputSimulator.SetVertical(1f);
        // 一フレームだけ待機して移動させる
        yield return null;  // Update が一度実行される
        Vector3 endPos = go.transform.position;
        Assert.That(endPos.z, Is.GreaterThan(startPos.z), "前進入力で Z 軸正方向に移動するはず");
        Assert.That((endPos - startPos).magnitude, Is.InRange(0.01f, 0.2f), "移動距離が speed * Time.deltaTime 程度であることを検証");
    }
}
例3: UI ボタン押下後の画面遷移テスト
テスト概要
- メインメニュー画面の「Start」ボタンをクリックし、GameScene へ遷移することを検証
 
テストコード例テストコード例
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
public class MenuFlowTests
{
    [UnityTest]
    public IEnumerator ClickStartButton_LoadsGameScene()
    {
        // メニューシーンを同期読み込み
        SceneManager.LoadScene("MenuScene");
        yield return null;
        // ボタン取得
        var buttonGO = GameObject.Find("StartButton");
        Assert.IsNotNull(buttonGO, "StartButton がシーン上に存在すること");
        var button = buttonGO.GetComponent<Button>();
        // SceneManager.sceneLoaded で遷移を待機
        bool sceneLoaded = false;
        SceneManager.sceneLoaded += (sc, mode) => {
            if (sc.name == "GameScene") sceneLoaded = true;
        };
        // ボタンのクリックをエミュレート
        button.onClick.Invoke();
        yield return new WaitUntil(() => sceneLoaded);
        Assert.AreEqual("GameScene", SceneManager.GetActiveScene().name, "ボタン押下後 GameScene に遷移すること");
    }
}
これらの詳細例を参考に、
- シーンの読み込み・イベント待機、
 - フレーム単位での入力/移動シミュレーション、
 - UI イベントのエミュレート
 
などを組み合わせて、実際のゲーム挙動を自動化テストできます。
3. CI/CD パイプラインへの組み込み
目的: Git コミット/プルリク時に自動でテストを実行し品質を担保
- GitHub Actions / Azure Pipelines / Jenkins などを設定
 - プッシュ時に Edit Mode & Play Mode テストを自動実行
 - すべてのテスト合格をデプロイ条件に
 
4. リファクタリング時の安全網として
目的: コードを大きく書き換える前に「壊れてはいけない動作」をテストで定義
- 既存機能の振る舞い固定
- リファクタリング後も同じテストが通ることを確認
 
 - ドキュメント代わりに
- テスト名・アサーションで仕様を明文化
 
 
5. バグ修正後の回帰防止
目的: 発見したバグをテストケース化し、再発を防ぐ
- Issue 再現シナリオを UnityTest メソッドに記述
 - 修正後にテストを通して確認
 - 将来の変更でも同じバグが発生しないことを保証
 
おすすめのはじめ方
- 小さな機能からテストを書く
- まずはシンプルな計算メソッドから
 
 - Edit Mode/Play Mode を使い分ける
- シーン依存なし → Edit Mode
 - ゲーム挙動 → Play Mode
 
 - テストの独立性を保つ
- 外部依存(I/O/ネットワーク)はモック化
 
 - 継続的に実行する
- エディタ起動時・CI に組み込んで自動化
 
 
これらの使い所を押さえれば、Unity の品質向上と開発効率アップを初心者のうちから体感できます。ぜひ小さく始めて、徐々にカバレッジを広げてみてください!
参考)その他の用途
Edit Mode テストの使い所例
- ダメージ計算ロジックの検証
- 例: 
DamageCalculator.Calculate(int attack, int defense)が期待通りのダメージを返すか 
 - 例: 
 - 経験値/レベルアップ計算
- 例: 
ExperienceCalculator.GetRequiredXP(int currentLevel)が次レベルに必要な経験値を正しく算出するか 
 - 例: 
 - コンボシーケンス判定
- 例: 
ComboValidator.IsValidSequence(List<Input> sequence)が正しいキー入力列をコンボと認識するか 
 - 例: 
 - クールダウン管理
- 例: 
CooldownTimer.IsReady(string skillId)がスキル再使用可能タイミングを正しく判定するか 
 - 例: 
 - パスファインディングアルゴリズム
- 例: 
PathFinder.FindPath(Vector2 start, Vector2 goal)が最短経路を返しているか 
 - 例: 
 - リーダーボードソート
- 例: 
LeaderboardManager.Sort(List<PlayerScore> scores)がスコア降順に正しくソートするか 
 - 例: 
 - 設定ロード/バリデーション
- 例: 
SettingsLoader.Load()が設定ファイル未存在時にデフォルト値を返すか 
 - 例: 
 - データ暗号化/復号
- 例: 
EncryptionService.Encrypt(string data)→Decrypt()で元の文字列に戻るか 
 - 例: 
 - テキストローカライズ
- 例: 
Localization.GetText(string key)が指定 locale の翻訳を正しく返すか 
 - 例: 
 - メタデータ生成
- 例: 
MapMetadataGenerator.Generate(int width, int height)がタイル数や障害物配置を含むメタ情報を生成するか 
 - 例: 
 
Play Mode テストの使い所例
- キャラクター移動の検証
- 例: ジョイスティック入力をシミュレートし、2 秒後のワールド座標が期待値と一致するか
 
 - コライダー&物理挙動テスト
- 例: プレイヤーがジャンプして床に着地するまでの落下時間と衝突判定を確認
 
 - UI ボタン操作と画面遷移
- 例: “Start” ボタンを押す → ゲームシーンがロードされ、メインカメラが有効化されるか
 
 - アニメーションステート遷移
- 例: Enemy の 
Animatorを再生 → Idle → Attack → Die の各ステートに正しく移行するか 
 - 例: Enemy の 
 - サウンド再生検証
- 例: ダメージを受けたときに指定の SoundClip が一度だけ再生されるか
 
 - パーティクルエフェクトの発生
- 例: 弾丸が敵にヒットしたときに Explosion の ParticleSystem が起動し、指定時間アクティブか
 
 - ゲームオーバー/リスタートフロー
- 例: プレイヤー HP が 0 以下 → GameOver UI が表示され、リスタートボタン押下で最初のシーンに戻るか
 
 - セーブ&ロード動作
- 例: プレイ中にセーブ → ゲームを再起動してロード → キャラクターの位置・ステータスが保持されるか
 
 - カメラフォロー機能
- 例: プレイヤー移動中にカメラが適切なオフセットで追従し続けるか
 
 - マルチプレイヤー同期テスト
- 例: NetworkManager をモックし、別クライアントの位置情報が正しく更新されるか
 
 
訪問数 29 回, 今日の訪問数 1回





ディスカッション
コメント一覧
まだ、コメントがありません