単体テストライブラリ「xUnit.net」を使ったユニットテストの基本的な実装方法を解説します。
環境
- Visual Studio 2022
- .NET 6
前提
簡単なサンプルアプリ(コンソールアプリ)を用いて説明します。
「CalculatorApp」というプロジェクトの中の「Calculator.cs」にテスト対象となるメソッドを用意しました。
中身は足し算を行うだけのシンプルなメソッドです。
namespace CalculatorApp
{
public class Calculator
{
public int Add(int x, int y)
{
return x + y;
}
}
}
単体テストプロジェクトの追加と参照の追加
まず単体テストプロジェクトを追加します。
ソリューション名を右クリックし、「追加」→「新しいプロジェクト」を選択してください。
プロジェクトの種類を「テスト」に変更し、一覧に表示された「xUnit テスト プロジェクト」を選択します。
プロジェクト名は「テスト対象のプロジェクト.Tests」とすることが多いようなので、今回は「CalculatorApp.Tests」としました。
フレームワークには.NET 6を選択しています。
作成されたxUnit テストプロジェクトを見てみると、自動でxUnit本体や関連ライブラリがインストールされていることが確認できます。
次に、テストプロジェクトからテスト対象のプロジェクトを参照するための設定を行います。
「CalculatorApp.Tests」の「依存関係」を右クリックし、「プロジェクト参照の追加」をクリックしてください。
参照マネージャー画面で、テスト対象のプロジェクトにチェックを入れてOKをクリックします。
プロジェクト参照が追加されました。
ユニットテストの実装
デフォルトで作成された「UnitTest1.cs」の名前を「Calculator.Test」に変更し、そこにAddメソッドのテストコードを実装していきます。
基本的なテスト
まずは渡した2つの引数が正しく合計されるかを確認するシンプルなテストを記述します。
[Fact]
public void AddTest()
{
// Arrange(準備)
var calculator = new Calculator();
// Act(実行)
var actual = calculator.Add(1, 2);
// Assert(確認)
Assert.Equal(3, actual);
}
xUnitの場合はテストメソッドにFact属性を付けます。
コメントに記載しましたが、テストコードは一般的にAAAパターンで記載することになっています。
AAAパターンは下記の3つのフェーズで構成されます。
- Arrange(準備):テスト対象のオブジェクト等を準備するフェーズ
- Act(実行):テスト対象のメソッドを実行するフェーズ
- Assert(確認):実行結果が想定通りであることを確認するフェーズ
今回はArrangeでテスト対象となるCalculatorクラスのインスタンスを生成し、Actで対象のAddメソッドを実行、Assertでその結果が期待値と一致するかどうかを確認しています。
今回のケースはとてもシンプルなのであまり恩恵はないかもしれませんが、複雑なメソッドをテストする際はAAAパターンを意識すると読みやすいテストコードを書くことができます。
それではテストを実行してみましょう。
テストプロジェクトを右クリックして「テストの実行」をクリックするとテストが実行されます。
このように緑のチェックマークが付けばテスト成功です。
ちなみに期待値等をあえて失敗するように変更してテストを実行すると、テストが失敗したことが赤いバツ印で表示されます。
複数パラメータを使ったテスト
先ほどは1パターンのパラメータと期待値をテストしましたが、1つのテストメソッドで複数パターンのテストを行いたい場合はInlineData属性とTheory属性(Fact属性ではないので注意)を使います。
[Theory]
[InlineData(1, 2, 3)]
[InlineData(5, 5, 10)]
public void AddTest2(int x, int y, int expected)
{
var calculator = new Calculator();
var actual = calculator.Add(x, y);
Assert.Equal(expected, actual);
}
InlineData属性に渡したいパラメータと期待値を指定すると、テストメソッドの引数にそれらが順番に渡されます。(InlineData(1, 2, 3)の場合は、引数xに1、引数yに2、引数expectedに3が渡される)
このようにすることで、テストメソッドを分けなくても効率的にテストすることができます。
なお、InlineDataに指定できるのはリテラル(数値や文字列)、定数、typeof演算子のみです。
リストなどでテストデータを渡したい場合は次に紹介する方法を使います。
コレクションを使ったテスト
テストデータにコレクションを使う場合はMemberData属性を使い、コレクションを生成するメソッドやプロパティを別に定義します。
[Theory]
[MemberData(nameof(GetNumbers))]
public void AddTest3(int x, int y, int expected)
{
var calculator = new Calculator();
var actual = calculator.Add(x, y)
Assert.Equal(expected, actual);
}
// MemberDataに渡すためのコレクションを作成
public static IEnumerable<object[]> GetNumbers()
{
return new List<object[]>
{
new object[] { 1, 2, 3 },
new object[] { 4, 3, 7 },
new object[] { 5, 5, 10 },
};
}
MemberData属性にコレクションを生成するメソッドの名前をnameof演算子で指定することで、コレクションの値をパラメータとして渡すことができます。
なお、渡せるメソッドには下記のような制約があります。
- publicかつstaticであること
- 戻り値がIEnumerable<object[]>であること
テストデータの件数が多い場合は、InlineData属性よりもMemberData属性を使った方がスッキリと書くことができます。
参考ドキュメント
dotnet テストと xUnit を使用した .NET Core での単体テスト C#
.NET Core と .NET Standard での単体テストのベスト プラクティス
参考書籍
単体テストに特化した技術書です。サンプルコードにはC#やxUnitが使われているので、単体テストの実装方法や考え方を学びたいC#エンジニアには特におすすめです。