【Unity】テストダブルを簡単に作れる.NETライブラリ「Moq」をUnityで使う手順

テストダブルを簡単に作れるライブラリ「Moq」をUnityで使う手順です

Moq 4.20.72
Unity 2022.3.28f1

Moqとは?

Moq を使うと、インターフェースを実装したテストダブルを簡単に作成することができます。

本来は以下のようにインターフェースを実装したテストダブルクラスをいちいち作らなければいけないところを・・

using Moq;
using NUnit.Framework;

public sealed class ExampleTest
{
    public class MockService : IService
    {
        public string Name => "MockService";

        public int GetValue()
        {
            return 5;
        }
    }
    
    [Test]
    public void GetMultipliedValue_UsingMockService()
    {
        var service = new MockService();
        var example = new Example(service);
        
        var multipliedValue = example.GetMultipliedValue();
        
        Assert.That(multipliedValue, Is.EqualTo(10));
    }
}

以下のようにテストダブルクラスを実装することなくテストを書くことができます。

using Moq;
using NUnit.Framework;

public sealed class ExampleTest
{
    [Test]
    public void GetMultipliedValue()
    {
        // インターフェースが実装されたモック(を管理するオブジェクト)を作成
        var serviceMock = new Mock<IService>();
        // GetValue メソッドが 5 を返すようにモックを設定
        serviceMock.Setup(x => x.GetValue()).Returns(5);
        var example = new Example(serviceMock.Object); // Object プロパティを使用してテストダブルを取得
        
        var multipliedValue = example.GetMultipliedValue();
        
        Assert.That(multipliedValue, Is.EqualTo(10));
    }
}

本記事ではこの Moq を Unity で使う方法についてまとめます。

インストール

Moq は NuGet に公開されているので、DLL の形で Unity にインストールする必要があります。

NuGet for Unity などでインストールしてもいいと思いますが、今回は直接DLLを入れていきます。
まず、Moq とその依存ライブラリは以下の通りです。

www.nuget.org

www.nuget.org

www.nuget.org

DLL をダウンロードするには、まず NuGet のページ右下の Open in NuGet Package Explorer をクリックします。

Open in NuGet Package Explorer

フォルダ構造が表示されるので、netstandard2.0 以下にあるDLLをダブルクリックしてダウンロードします。

ダウンロード

他のライブラリについても同様の手順でダウンロードできます。

次に、ダウンロードしたDLLをUnityの適当なフォルダにインポートします。
テストでしか使わないものなので、インポート設定は以下のように、Auto Reference を外して、Define Constraints に UNITY_INCLUDE_TESTS を設定しておきます。

インポート設定

テスト用アセンブリをセットアップ

次にテスト用のアセンブリをセットアップします。

アセンブリ自体の作り方は割愛しますが、以下の記事にまとめていますので必要に応じて参照してください。

light11.hatenadiary.com

アセンブリを作成したら、Assembly References に Moq.dll を加えます。

Moq.dll

テスト対象のアセンブリスクリプトを作成

セットアップが完了したら、次にテスト対象のアセンブリスクリプトを適当に作成しておきます。

今回は以下のスクリプトを作成しました。

public interface IService
{
    string Name { get; }

    int GetValue();
}

public class FooService : IService
{
    public string Name => "FooService";

    public int GetValue()
    {
        return 123;
    }
}

public class Example
{
    private readonly IService _service;

    public Example(IService service)
    {
        _service = service;
    }

    public string GetServiceName()
    {
        return _service.Name;
    }

    public int GetMultipliedValue()
    {
        return _service.GetValue() * 2;
    }
}

なお、Internal なものをテストする際には InternalsVisibleTo アトリビュートにテスト用のアセンブリ名を指定します。

light11.hatenadiary.com

が、Moq を使う場合にはさらに DynamicProxyGenAssembly2 も指定する必要があるので、internal なものをテストする際には忘れないようにしてください(忘れてもエラーで気づけます)。

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Example.Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Moqのために必要

テストを作成する

次に、前節で作ったセンブリをテスト用のアセンブリAssembly Definition Reference に追加しつつテストを書いていきます。

using Moq;
using NUnit.Framework;

public sealed class ExampleTest
{
    [Test]
    public void GetServiceName()
    {
        // インターフェースが実装されたモック(を管理するオブジェクト)を作成
        var serviceMock = new Mock<IService>();
        // Name プロパティが「MockService」という値を返すようにモックを設定
        serviceMock.SetupGet(x => x.Name).Returns("MockService");
        var example = new Example(serviceMock.Object); // Object プロパティを使用してテストダブルを取得
        
        var serviceName = example.GetServiceName();
        
        Assert.That(serviceName, Is.EqualTo("MockService"));
    }
    
    [Test]
    public void GetMultipliedValue()
    {
        var serviceMock = new Mock<IService>();
        // GetValue メソッドが 5 を返すようにモックを設定
        serviceMock.Setup(x => x.GetValue()).Returns(5);
        var example = new Example(serviceMock.Object);
        
        var multipliedValue = example.GetMultipliedValue();
        
        Assert.That(multipliedValue, Is.EqualTo(10));
    }
}

FooService という実装クラスを使うことなく、インターフェースを通したテストをモックオブジェクトを使ってできることが確認できました。

その他の使い方

その他の使い方については公式のドキュメントがわかりやすいので本記事では説明しません。
必要に応じて以下を参照してください。

github.com

参考

github.com