MVC ビューを単体テスト
ちょっと勘違いしてたかも。とりあえず全部削除。
今回もフレームワークじゃなくて、パターンの方の話。
# フレームワークなら、ビューも簡単に単体テストできるのかな?
例えばこの記事に掲載した後者のコードに含まれる Lend というページクラス。ILendView の実装部分に関しては、サーバーコントロールのプロパティ設定程度しかやっていないわけで、一見するとこれは単体テストが簡単にできそうな気がする。
しかし、ページクラスのテストには大きな問題が 2 つある。
1. ASPX のコンパイル
普段僕たちがプログラミングしているページクラスはコードビハインドによって ASPX から分離されたクラスであり、コントロールツリーの生成処理は記述されていない。これは、ページクラスを継承し、ASPX ファイルに記述されているコントロールツリーを適切に生成するためのクラスが別に用意されるためだ。通常、ASPX のコンパイルは実行時に行われる。
2. HttpContext の用意
通常、ページの初期化を行うには HttpContext が必要となるのだが、これを自分でこしらえるのは容易ではないようだ。
問題 1 については、プリコンパイルの設定でなんとかできた。これには Web Deployment Project (リンク先は VS 2008 用なので注意) が必要。
問題 2 については、HttpContext を使わないでコントロールツリーを生成させることに成功した。具体的には、コンパイルされた ASPX に自動で定義される「__BuildControlTreeMethod」というプライベートメソッドをリフレクションで呼び出すことで、コントロールツリーの生成処理のみを行うことができる。
以下、僕がやった手順。
[ 1 ] ASP.NET Web アプリケーションプロジェクトの用意
public void Hoge() { this.TextBox1.Text = "Hoge"; }
[ 2 ] Web 配置プロジェクトの用意
[ 3 ] 単体テストの用意
[Test] public void Hoge() { ASP.default_aspx page = new ASP.default_aspx(); MethodInfo __BuildControlTreeMethod = typeof(ASP.default_aspx).GetMethod("__BuildControlTree", BindingFlags.Instance | BindingFlags.NonPublic); __BuildControlTreeMethod.Invoke(page, new object[] { page }); TextBox tb = (TextBox)page.FindControl("TextBox1"); Assert.That(tb.Text, Is.Empty, "Hoge する前はテキストボックスが未入力であるか"); page.Hoge(); Assert.That(tb.Text, Is.EqualTo("hoge"), "Hoge した後はテキストボックスが Hoge であるか"); }
これで、サーバーコントロールのプロパティ設定程度の処理は単体テストできるはず。
[ おまけ ]
public static TPage CreatePage<TPage>() where TPage : Page, new() { TPage page = new TPage(); MethodInfo __BuildControlTreeMethod = typeof(TPage).GetMethod("__BuildControlTree", BindingFlags.Instance | BindingFlags.NonPublic); __BuildControlTreeMethod.Invoke(page, new object[] { page }); return page; }
こんなメソッドを用意しておけば、ページの準備を
ASP.default_aspx page = CreatePage<ASP.default_aspx>();
というふうに、簡単に記述できる。