xUnit
で複雑なテストデータを作成する場合、ClassData
を使って別途テストデータ作成用のクラスを用意したりしますが、このテストデータ作成用のクラスでテストクラス内のリソースを操作したいことがあったので、思いついた案を載せておきます。
ここでいうリソースとは、例えばテスト用のデータベースだとか、外部ファイルなどです。
あくまで私が思いついた一例ですが、概要としては、以下です。
- テストメソッドの引数にデリゲートを用意し、テストメソッド内からリソースを渡す
- テストデータ作成用のクラスで、リソースに対する処理を記述
環境
- Visual Studio 2017
- .NET Core 2.2
サンプルコード
resource
がデータベースなどの外部のリソースを想定しています。
テスト対象のクラス
以下のようなクラスをテストしたいとします。
// テストしたいクラス
public class Target
{
// テストしたいメソッド
public int Add(IList<int> resource)
{
// 例えば、データベースなどから値を読み込んで、何か処理をした値を返すなど
// ここでは全レコードを読み込んで総和を返すようなことを想定
return resource.Sum();
}
}
テストコード
以下がテストコードです。
解説はコメントに書いてある通りです。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Xunit;
using Xunit.Abstractions;
namespace TestProject
{
// テストを行うクラス
public class TestClass
{
ITestOutputHelper output;
Target target;
readonly IList<int> resource;// 例えば、データベースやファイルなどの外部リソース
public TestClass(ITestOutputHelper output)
{
// デバッグ用にテストエクスプローラーに出力できるようにしておく
this.output = output;
// リソースの初期化(実際には IClassFixture を使うなどする)
resource = new List<int> { 1, 2 };
// テストクラスのインスタンスを作成
target = new Target();
}
[Theory,
ClassData(typeof(TestData))]
public void TestMethod(
// テスト前にリソースに対して行う処理(戻り値が必要なら Func にする)
Action<IList<int>> action,
// テスト実行後の期待値
int expected
)
{
// テストに必要な前処理を行う
action(resource);
// デバッグ用にテスト前のリソースの状態を出力
output.WriteLine($"resource: {string.Join(", ", resource)}");
// テストの実行
var result = target.Add(resource);
// デバッグ用に結果を出力
output.WriteLine($"result: {result.ToString()}");
// 結果の確認
Assert.Equal(expected, result);
}
}
// テストデータを作成するクラス
class TestData : IEnumerable<object[]>
{
List<object[]> _testData = new List<object[]>();
public TestData()
{
// いくつかのパターンでテストデータを作成してみる
// テストケース1(リソースの中身を書き換え)
Action<IList<int>> action = resource =>
{
resource[0] = 1;
resource[1] = 2;
};
_testData.Add(new object[] {
action,
3
});
// テストケース2(リソースに要素を追加)
_testData.Add(new object[] {
new Action<IList<int>>(resource => resource.Add(3)),
6
});
// テストケース3(リソースをクリア)
action = resource => resource.Clear();
_testData.Add(new object[] {
action,
0
});
}
public IEnumerator<object[]> GetEnumerator() => _testData.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
実行結果
テストケース1
resource: 1, 2
result: 3
テストケース2
resource: 1, 2, 3
result: 6
テストケース3
resource:
result: 0