モバイル実機でUnity Test Frameworkを実行する

Unity

はじめに

ジークレストのクライアントエンジニア、森と申します。UnityのクライアントサイドのテストではUnity Test Frameworkを使うケースが多いと思います。Unity Test Frameworkはモバイル実機で実行できるテストもサポートされていますので、今回はモバイル実機でUnity Test Frameworkを実行する手順を解説していこうと思います。

Unity Test Frameworkとは

UnityPlatform attribute | Test Framework | 1.3.9

Unity Test Framework(旧名称 “Unity Test Runner”)はUnity公式から出ているテストツールで、この機能を使ってテストを記述しているプロジェクトも多いと思います。

Edit Mode Tests と Play Mode Tests

Unity Test FrameworkにはEditモードとPlayモードの2つが存在しています。EditモードではUnityのエディタ画面でテストを実行できます。エディタからシームレスに実行できるので、ゲームロジックチェック等の基本的なテストはEditモードで記述するのがおすすめです。

Playモードのテストを実行すると、UnityがPlayモードになりコルーチンとしてテストが実行されます。どうしても非同期実行が必要な処理であったり、ゲーム内の複数の機能やモジュールの挙動を確認したいというEndToEndのテストを記述したい場合にはPlayモードのテストを利用します。

記事内の実行環境

さてこれからサンプルスクリプトを交えてUnity Test Frameworkの挙動を解説しますが、スクリプトや挙動はこちらの環境で実行しています。

  • Unity 2020.3.42f1
  • Android 13 (Pixel 6)
  • iOS 13.3.1 (iPhone XS)

Edit Mode Testsのサンプル

Edit Mode Testsのスクリプトは以下のように記述します。Testしたいメソッドには [Test] のアトリビュートをつける必要があるのにご注意ください。

using System;
using NUnit.Framework;

public class EditModeTestScript
{
    [Test]
    public void SampleTest()
    {
        var sum = 1 + 1;
        Assert.IsTrue(sum == 2, "Sum is not 2!");
    }
}

Play Mode Testsのサンプル

Play Mode Testsのスクリプトは以下のように記述します。Testしたいメソッドには [UnityTest] のアトリビュートをつける必要があるのにご注意ください。

using System;
using NUnit.Framework;

public class PlayModeTestScript
{
    [UnityTest]
    public IEnumerator SampleTest()
    {
        yield return null; // コルーチンを使って1F待つ
        var sum = 1 + 1;
        Assert.IsTrue(sum == 2, "Sum is not 2!");
    }
}

Play Mode Testsはコルーチン実行されているので、テスト内でコルーチンを記述できます。今回は意味のないコルーチンになっていますが、非同期処理が必要な処理を記述する場合にはコルーチンが非常に役立ちます。例えばコルーチンでライブラリの起動を待ち起動後にテストを実行することといった記述が可能です。実際の利用ケースとしては「ライブラリを起動する」→「起動処理をコルーチンで待機する」→「起動処理後にテスト実行」という流れの処理を記載するケースが多いでしょう。

(小ネタ)Assertの出し方について

Constraint Model (Assert.That) | NUnit Docs
Classic Model | NUnit Docs

Unity Test Frameworkに使用されているNUnitのAssertionの出し方には制約モデルとクラシックモデルの2つがあるようです。NUnitのドキュメントにも記載されてありますが、クラシックモデルはまたサポートしているものの制約モデルのほうが機能がアップデートされているとのことです。なるべく制約モデルを採用するようにしましょう。

In NUnit 3.0, assertions are written primarily using the Assert.That method, which takes constraint objects as an argument. We call this the Constraint Model of assertions.

In earlier versions of NUnit, a separate method of the Assert class was used for each different assertion. This Classic Model is still supported but since no new features have been added to it for some time, the constraint-based model must be used in order to have full access to NUnit’s capabilities.

https://docs.nunit.org/articles/nunit/writing-tests/assertions/assertions.html

Editモードテストで記述した、和の結果をテストするスクリプトを制約モデルに書き換えると以下のようになります。

using System;
using NUnit.Framework;

public class EditModeTestScript
{
    [Test]
    public void SampleTest()
    {
        var sum = 1 + 1;
        Assert.That(sum, Is.EqualTo(2), "Sum is not 2!");
    }
}

モバイル実機でテストスクリプトを実行してみる

さて本題のモバイル実機でのテストについてお話しします。Unityのモバイルゲーム開発をしていると、Android/iOSのネイティブ機能やGooglePlayServicesといったネイティブのAPIと連携した機能のテストを書きたくなることがあります。Unity Test Frameworkではプラットフォームごとにテストを記述できるアトリビュートが存在しています。

Unity Test Runner でのテストの作成と実行 - Unity マニュアル
Unity Test Runner は、スタンドアロン、Android、iOS などのターゲットプラットフォーム上で行うのと同様に、編集 モードと 再生 モードでコードをテストします。

[UnityPlatform (RuntimePlatform.WindowsPlayer)]

こちらのアトリビュートを関数に追加することでプラットフォームごとのテストが記述できます。

Androidでのテスト実行

サンプルのテストスクリプトはこちらです。ここではAndroidのネイティブ関数でディスク容量を確認する関数の実行テストを想定します。

#if UNITY_ANDROID
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

public class AndroidPlayModeTests
{
    [UnityTest]
    [UnityPlatform(RuntimePlatform.Android)]
    public IEnumerator GetTotalSpaceTest()
    {
        var value = AndroidPlugin.GetTotalSpace(); // AndroidPlugin.GetTotalSpace()はAndroidネイティブ関数を想定
        Assert.That(value > 0);
        yield return null;
    }
}
#endif

実際にテストスクリプトを実行してみましょう。まずはUnityのBuild SettingからPlatformをAndroidにしましょう。

図. PlatformをAndroidに更新

PlatformをAndroidにした状態でTest RunnerのWindowを開くと、右上部にAndroid用のデバッグメニューが存在します。今回はすべてのテストを実行するために「Run All Tests(Android)」をクリックします。

図. Run All Testsを実行

AndroidとPCの接続に問題なければ、プロジェクトのビルドとAndroidへの転送、Androidでの実行とテストが進んでいきます。

図. ビルド実行

正しく実行できるとConsoleとTest Runnerから実行結果が取得できます。

図. Console上のログ表示
図. テスト結果はTest Runnerの表示でも確認できる

前提としてUnityからAndroid実機ビルドができる状態にあることを確認しましょう。エラーになる場合はAndroid実機の状態やドライバ等に不備があるかもしれません。

iOSでのテスト実行

iOSのスクリプトは [UnityPlatform(RuntimePlatform.OSXPlayer)] のアノテーションを関数へ付与します。ただしiOS実機で実行するためにはXcodeでのビルドが必要で、Androidと挙動が変わります。以下に手順を説明します。

まずはUnityのBuild SettingからPlatformをiOSに変更します。

図. PlatformをiOSへ更新

Test RunnerのWindowを開くと、右上部のTestRunnerのメニューに「Run All Tests(iOS)」と表示されます。こちらをクリックするとUnityのビルドが実行され、Xcodeのプロジェクトが吐き出されます。

図. Run All Testsを実行
図. ビルド実行

ビルドが成功するとXcodeプロジェクトが自動で立ち上がります。Xcodeのプロジェクトを開いた状態でiPhoneを接続し、証明書やバンドルIDを適切に設定します。この状態で実行できるはずなのですが、私の環境ではXcodeのSchema設定からDebug Executableのチェックを外す必要がありました。設定更新後にXcodeからビルドを実行します。

図. Xcodeが自動で立ち上がる
図. SchemaからDebug Executableを無効にする

テストが実行されるとiPhoneでテスト用Unityアプリが起動します。起動後にテストが実行され、画面上からテストが成功しているか失敗しているかを確認できます。

図. iPhone実機上でテストが実行される

iOSテストは実機でしか確認できないのが億劫ではありますが、iPhone実機の機能を検証するには取り回しが良いと感じています。iOSと連携した機能の挙動がiOSネイティブライブラリを使用する機能のテストを作るにはとても使い勝手が良さそうです。

まとめ

今回はモバイル実機でUnity Test Frameworkを実施する方法を解説しました。モバイルゲームアプリを開発していると、iOSやAndroidのネイティブ機能を呼び出す機能がどうしても存在してしまいます。ネイティブ機能についてテストが用意されていると安心して開発を進められるかもしれません。