[.NET Core] Moq と DI

.NET Core で Moq と DI を使ってテストコードを実装する。

# 環境
- Windows 10 Pro
- Visual Studio 2019
- .NET Core 3.1

# 準備
- Visual Studio で 「xUnit テストプロジェクト（.NET Core）」を作成
- 作成したテストプロジェクトに、NuGet パッケージの管理から「Moq」をインストール

# 実装
## サンプルコード
``` Hoge.cs
    public interface IHoge
    {
        public int Value { get; set; }
        public int Calculate(int value);
        public int Calculate();
    }

    public class Hoge : IHoge
    {
        public int Value { get; set; }

        public int Calculate(int val) => val * 2;

        public int Calculate() => Calculate(Value);
    }
```
``` Fuga.cs
    public class Fuga
    {
        private IHoge hoge;

        public Fuga(IHoge hoge) => this.hoge = hoge;

        public int Calculate(int val) => hoge.Calculate(val);

        public int GetValue() => hoge.Value;

        public int Calculate() => hoge.Calculate();
    }
```
## テストコード
``` UnitTest.cs
    public class UnitTest
    {
        private Mock<IHoge> hogeMock = new Mock<IHoge>();

        [Fact]
        public void Test1()
        {
            // Hoge.Calculate(int val) の戻り値に 6 を設定
            hogeMock.Setup(h => h.Calculate(It.IsAny<int>()))
                .Returns(6);

            var f = new Fuga(hogeMock.Object);
            Assert.Equal(6, f.Calculate(1));
        }
```
- Test1
  - 引数の値に関わらず、固定で Calculate メソッドの戻り値を設定する。

``` UnitTest.cs
        [Fact]
        public void Test2()
        {
            // Hoge.Calculate(int val) 引数が 4 以下の場合、戻り値に 10 を設定
            hogeMock.Setup(h => h.Calculate(It.Is<int>(i => i <= 4)))
                .Returns(10);
            // Hoge.Calculate(int val) 引数が 5 以上の場合、戻り値に 20 を設定
            hogeMock.Setup(h => h.Calculate(It.Is<int>(i => i >= 5)))
                .Returns(20);

            var f = new Fuga(hogeMock.Object);
            Assert.Equal(20, f.Calculate(5));
        }
```
- Test2
  - 引数の値によって、Calculate メソッドの戻り値を分岐させる。

``` UnitTest.cs
        [Fact]
        public void Test3()
        {
            // Hoge.Calculate(int val) ApplicationException が発生
            hogeMock.Setup(h => h.Calculate(It.IsAny<int>()))
                .Throws<ApplicationException>();

            var f = new Fuga(hogeMock.Object);
            Assert.Throws<ApplicationException>(() => f.Calculate(5));
        }
```
- Test3
  - Calculate メソッドにて例外が発生した状態とする。

``` UnitTest.cs
        [Fact]
        public void Test4()
        {
            // Hoge.Value に 4 を設定
            hogeMock.SetupProperty(h => h.Value, 4);

            var f = new Fuga(hogeMock.Object);
            Assert.Equal(4, f.GetValue());
        }
```
- Test4
  - Value プロパティに値を設定する。

``` UnitTest.cs
        [Fact]
        public void Test5()
        {
            // Hoge.Value setter に 3 を設定
            hogeMock.SetupSet(h => h.Value = 3);
            // Hoge.Value getter に 5 を設定
            hogeMock.SetupGet(h => h.Value)
                .Returns(5);

            var f = new Fuga(hogeMock.Object);
            // GetValue() で呼び出されるのは Hoge.Value getter
            Assert.Equal(5, f.GetValue());
        }
```
- Test5
  - Value プロパティの setter と getter に異なる値を設定する。
  - Fuga.GetValue() は　Hoge.Value の値を取得するため、Mock の getter が有効となる。

``` UnitTest.cs
        [Fact]
        public void Test6()
        {
            // Hoge.Calculate() の戻り値に 8 を設定
            hogeMock.Setup(h => h.Calculate())
                .Returns(8);
            // Hoge.Calculate(int val) の戻り値に 6 を設定
            hogeMock.Setup(h => h.Calculate(It.IsAny<int>()))
                .Returns(6);
            // Hoge.Value に 4 を設定
            hogeMock.SetupProperty(h => h.Value, 4);

            var f = new Fuga(hogeMock.Object);
            // Calculate() の戻り値に 8 を設定しているため、内部処理（Calculate(int val)、Value）の設定は意味を持たない
            Assert.Equal(8, f.Calculate());
        }
```
- Test6
  - 引数なし Calculate メソッド、引数あり Calculate メソッド、Value プロパティに値を設定する。
  - Fuga.Calculate() は　Hoge.Calculate() の値を取得するため、Mock の引数なし Calculate メソッドが有効となる。
  - Mock の引数あり Calculate メソッド、Value プロパティの値が利用されていないことが確認できる。

# 使いどころ
- Controller など、依存するモジュールが多いクラスのテスト実装の際に特に便利。
- パラメータによる戻り値の分岐や、例外発生などができるため、広範囲にテストが実施できる。
- より複雑なことをしたい場合は、下記サイトをより詳細に読み込むのが良し。

# Log の Verify について
別記事書きました
[[.NET Core] xUnit で log を verify する](https://qiita.com/pi2ji79/items/ec54afbd31cac24a17c9)

# 参考
- https://github.com/Moq/moq4/wiki/Quickstart