前回までのあらすじ
ねんがんの
xUnitを
どうにゅうしたぞ!
最初の記事で後回しにしていたことを回収する
最初の記事で、ControllerにNameプロパティを生やしたときに「その整合性はユニットテストで検証する」としたので、それをチェックしようと思います。
Nameプロパティはコントローラーの名前を返すようにしておきます。 このNameプロパティが返す文字列は"HomeController"の接尾語"Controller"を覗いた部分と常に一致させる必要があります。 力ずくの解決策なので、整合性チェックは単体テストですることにします。
ASP.NET Core MVC で、コントローラー名とアクション名とかをインテリセンスに無理矢理対応させる Part.1 - 時が癒す
テスト手順
- リフレクションを用いて、テストターゲットのアセンブリ情報を読み込む
- 1より、Controller型を継承しているコントローラーの情報を全て取得する
- 2で取得したコントローラーに、Nameプロパティがあるか調べる。(今回は無い場合もエラーとする)
- 3のNameプロパティの値(+"Controller")が、2で取得したコントローラーの名前と同じか調べる。
アセンブリ情報を読み込む
「Assembly.LoadFrom()でアセンブリ情報読み込めばええやろー」と思っていたら、なんと.NET COREには"Assembly.LoadFrom()"がありません!
「AssemblyLoadContextを継承したAssemblyLoaderを作りたまえ!」という事らしいので、適当に作りました。
public class AssemblyLoader : AssemblyLoadContext { protected override Assembly Load(AssemblyName assemblyName) { return Assembly.Load(assemblyName); } }
それと、次のようなUtiityも併せて作りました。
public static class TestUtils { public static IEnumerable<Type> FindAllDerivedTypes<T>(string name) { var loader = new AssemblyLoader(); var asm = loader.LoadFromAssemblyName(new AssemblyName(name)); return FindAllDerivedTypes<T>(asm); } public static IEnumerable<Type> FindAllDerivedTypes<T>(Assembly assembly) { var derivedType = typeof(T); return assembly.GetTypes() .Where(t => t != derivedType && derivedType.IsAssignableFrom(t)); } }
で、実行してみたら動作しました。やったね!
テストする
次に新しいテストクラス"ControllerTest"を作成して、テストを実装します。
手順は上記に書いた通りなので、ソースを張り付けておきます。
public class ControllerTest { public IEnumerable<Type> Controllers { get; set; } public ControllerTest() { Controllers = TestUtils.FindAllDerivedTypes<Controller>("PracticeWorld1"); } [Fact] public void AllControllersShouldHaveNamePropety() { foreach (var controller in Controllers) { var pi = controller.GetProperty("Name", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); Assert.True(pi != null, $"controller: {controller.FullName}"); var value = pi.GetValue(controller); var expected = controller.Name; var actual = value + "Controller"; Assert.Equal(expected, actual); } } }
これで、こんな風にNameプロパティを実装し忘れたり、
コピペしたまま直し忘れたり、
というヒューマンエラーを検出できます。
ここまですれば、cshtmlにcontroller="Home"と文字列で記述するよりも便利になると思います。
見た目はかっこよくはないかもしれませんが。
WebAPIのコントローラー?知らんよ。
ところで、xUnit.NETでは、Assert.TrueとAssert.FalseしかAssert失敗時のユーザーメッセージを与えられないのでしょうかね?