.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 する