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

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

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

# ■ライブラリ

- [Moq](https://www.nuget.org/packages/Moq/)
- [Autofac](https://www.nuget.org/packages/Autofac/) (v6)
- [Autofac.Extras.Moq](https://www.nuget.org/packages/Autofac.Extras.Moq)

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

## Autofac モジュール

ここでは、既定で protected プロパティに注入するように構成します。

```c#
internal class ProtectedPropertyInjectionModule : Autofac.Module
{
    private static readonly DelegatePropertySelector ProtectedSetterSelector =
        new DelegatePropertySelector((p, o) => p.CanWrite && (p.SetMethod?.IsFamily ?? false));

    protected override void AttachToComponentRegistration(
        IComponentRegistryBuilder componentRegistry,
        IComponentRegistration registration)
    {
        registration.PipelineBuilding += (sender, pipeline) =>
        {
            pipeline.Use(PipelinePhase.Activation, MiddlewareInsertionMode.EndOfPhase, (c, next) =>
            {
                next(c);

                c.InjectProperties(c.Instance, ProtectedSetterSelector);
            });
        };
    }
}
```

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

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

```c#
public static void StartApplication()
{
    var builder = new ContainerBuilder();

    builder.RegisterModule(new ProtectedPropertyInjectionModule());
    builder.RegisterType<Service>().SingleInstance();
    builder.RegisterType<Client>();

    var container = builder.Build();

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

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

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

```c#
public class Client
{
    /// <summary>
    /// 依存サービス。
    /// </summary>
    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例
/// <summary>
/// 単体テストの基底クラス。
/// </summary>
public abstract class AutofacTestBase
{
    delegate void BuilderAction(ContainerBuilder builder);

    private BuilderAction registerMocks;

    private List<Mock> mocks;

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

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

    /// <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.mocks.Add(mock);

        // RegisterMock はジェネリック引数（ここでは暗黙指定）から型を判定するので、登録処理自体をデリゲートに収める。
        // （List<Mock> では型を渡せない）
        this.registerMocks += builder => builder.RegisterMock(mock);

        return mock;
    }

    /// <summary>
    /// AutoMock オブジェクトを取得する。
    /// </summary>
    protected AutoMock GetLoose()
    {
        return AutoMock.GetLoose(builder =>
        {
            builder.RegisterModule(new ProtectedPropertyInjectionModule());
            this.registerMocks.Invoke(builder);
        });
    }
}

```

## テストクラス

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

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

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

        // モックのDI登録
        using (var autoMock = GetLoose())
        {
            // テスト対象オブジェクトを生成
            var client = autoMock.Create<Client>();

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

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

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

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