[.NET] 単体テストがさくっと書ける！モック化の枠組み（Moq + Unity）

モックライブラリ [Moq](https://github.com/moq) とDIコンテナ [Unity](https://github.com/unitycontainer/unity)[^1]を使用してモック化する例です。
単体テストの基底クラスにモックの生成、検証を抽出することで、Verify 漏れを防ぎ、素早く、すっきりテストコードを書くことができます。

※単体テストでDIコンテナを使用することの是非はここでは問いません。

# ■プロダクションコード

## アプリケーション開始処理

DIを構成し、アプリケーションを開始します。
※フレームワークに応じた適切な箇所に記述してください。

```c#
public static void StartApplication()
{
    var container = new UnityContainer();

    container.RegisterSingleton<Service>();
    container.RegisterType<Client>();

    var client = container.Resolve<Client>();
    var result = client.Act();

    Console.WriteLine(result);
    // Production
}
```

## クライアントクラス（System Under Test）

```c#
public class Client
{
    /// <summary>
    /// 依存サービス。
    /// </summary>
    [Dependency]
    protected Service Service { get; set; }

    /// <summary>
    /// テスト対象メソッド。
    /// </summary>
    public string Act()
    {
        // プロパティ経由でDIコンテナから依存オブジェクトを取得して使用する。
        return this.Service.GetText();
    }
}
```

## 依存サービス（Depended-On Component）

```c#
public class Service
{
    private readonly string text = "Production";

    public virtual string GetText()
    {
        return this.text;
    }
}
```

# ■単体テスト

## テストの基底クラス

モックオブジェクトの生成、DI登録、検証（Verify）を担います。

```c#:MSTest例
public abstract class UnityTestBase
{
    private List<Mock> mocks;

    private IUnityContainer diContainer;

    /// <summary>
    /// テストの初期処理。
    /// </summary>
    [TestInitialize]
    public void BaseTestInitialize()
    {
        this.diContainer = new UnityContainer();
        this.mocks = new List<Mock>();
    }

    /// <summary>
    /// テストの終了処理。
    /// </summary>
    [TestCleanup]
    public void BaseTestCleanup()
    {
        // モックが期待どおりに呼び出されたことを検証する。
        foreach (var mock in this.mocks)
        {
            mock.VerifyAll();
        }

        this.diContainer.Dispose();
    }

    /// <summary>
    /// Mock インスタンスを生成する。
    /// </summary>
    protected Mock<T> CreateMock<T>()
        where T : class
    {
        return CreateMock<T>(false);
    }

    /// <summary>
    /// Mock インスタンスを生成する。
    /// </summary>
    protected Mock<T> CreateMock<T>(bool callBase)
        where T : class
    {
        var mock = new Mock<T> { CallBase = callBase };

        this.diContainer.RegisterInstance(mock.Object);
        this.mocks.Add(mock);

        return mock;
    }

    /// <summary>
    /// テスト対象オブジェクトを構成する。
    /// </summary>
    protected void BuildUp<T>(T instance)
    {
        this.diContainer.BuildUp<T>(instance);
    }
}
```

## テストクラス

モックをDIコンテナに渡すので、依存コンポーネントを差し替えるためだけに Client を継承した Testable クラスを用意する必要はありません。

モックインスタンスは、個々のテストメソッドに必要なものだけ生成します。

```c#:MSTest例
[TestClass]
public class ClientTest : UnityTestBase
{
    [TestMethod]
    public void Act_Mocking()
    {
        // モック設定
        var serviceMock = CreateMock<Service>();
        serviceMock.Setup(s => s.GetText()).Returns("UnitTest");

        // テスト対象オブジェクトを生成
        var client = new Client();
        BuildUp(client);

        // テスト対象メソッドを実行
        string result = client.Act();

        // 戻り値の検証
        // ※モックの検証は基底クラスで自動的に行われる。
        Assert.AreEqual("UnitTest", result);
    }
}
```

---
※掲載コードはサンプルですので、実際のプロジェクトでは、細かい点を調整してご活用ください。

※Autofac 版は [こちら](https://qiita.com/CodeOne/items/04541f1dda346d7dd9f6) をご覧ください。

[^1]: ゲームエンジンの方ではありません。Microsoft patterns & practices のプロジェクトとして開発された軽量DIコンテナで、現在はその手を離れ、オープンソースで運営されています。