[C#] テストデータファイルをテストケースごとにディレクトリ分けして管理する
C#でUnitTest(MSTest)を書いていると、テスト用のデータファイルが必要な場合がある。
(画像を読み込んでなにかするとか、ヒアドキュメントで書くのは厳しいサイズのJSONがあるとか)
テストケースごとに必要なファイルが異なるので、"テストクラス/テストケース"という名前のディレクトリに分けて管理することにした。
(DeploymentItemAttributeはいまいち便利さが分からない…)
テストデータはVisualStudioから簡単に表示したいので、プロジェクトに登録し、ビルド時にデータファイルとして出力フォルダにコピーするよう設定した。
テストの実装側ではこうした。
[TestClass]
public class SomeTestClass {
[TestMethod]
public void SomeTest(){
// テストクラスやテストメソッドのリネームに追随できるよう、nameofを使ってパスを定義
var testDataDir = Path.Combine(nameof(SomeTestClass), nameof(SomeTest)); // <- テストケースごとに書きかえる必要がある
// ディレクトリ内のテストデータを読んでテストする
var imgPath = Path.Combine(testDataDir, "foo.png");
}
}
問題
nameof(SomeTestClass)のところを書き換えるのがとても面倒くさい。
対策
こんなクラスを作った。
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Diagnostics;
using System.Linq;
internal class TestHelper
{
/// <summary>
/// テストデータ格納ディレクトリへのパスを取得する。
/// </summary>
/// <returns>テストデータ格納ディレクトリへのパス。</returns>
public static string GetTestDataDir()
{
var testMethodStackFrame = new StackTrace()
.GetFrames()
.First(x => Attribute.GetCustomAttribute(x.GetMethod(), typeof(TestMethodAttribute)) != null);
if (testMethodStackFrame == null) throw new InvalidOperationException("This method must be called from a test method with the TestMethodAttribute attribute.");
var testMethod = testMethodStackFrame.GetMethod();
return $@"TestData\{testMethod.ReflectedType.Name}\{testMethod.Name}";
}
}
なにこれ
この関数を呼び出すと、以下が行われる。
- スタックトレースをたどって、TestMethod属性がついてるメソッド(= テストケース)を探す
- メソッド名とそのメソッドが定義されているクラス名を取得する
- "TestClass\TestMethod"形式のパス文字列を生成して返す
結果
全テストケースで、以下をコピペするだけで、対応ディレクトリを取得できるようになった。
var testDataDir = TestHelper.GetTestDataDir();
ほか
テストフレームワークがNUnitの場合は、TestMethodAttributeをTestAttributeに変えればそのまま動くはず。