同僚と私は、ASP.NET MVC 2アプリケーションのフォームに不正なデータを入力するときに、ユーザーがエラーを受け取ることを確認するための単体テストを作成する正しい方法について話し合っていました。モデルから始めて、過去に行ったことは次のとおりです。
public class LoginModel
{
public string Username { get; set; }
}
コントローラのアクションは次のとおりです。
[HttpPost]
public ActionResult Login( LoginModel loginModel )
{
if ( loginModel.Username == null )
{
ModelState.AddModelError( "Username", "Username is required!" );
return View( loginModel );
}
LoginService.Login( loginModel );
}
最後に、テスト方法は次のとおりです。
[TestMethod]
public void Login_Post_Blank_Username_Displays_Error()
{
var controller = GetHomeController();
var loginModel = new LoginModel
{
Username = null
};
var result = controller.Login( loginModel );
Assert.IsInstanceOfType( result, typeof( ViewResult ) );
var view = (ViewResult)result;
Assert.IsNotNull( view.ViewData.ModelState["Username"].Errors.First().ErrorMessage );
}
彼は、これはこの状況のテストを書くための正しい方法ではないことを私に指摘しました。理由の1つは、非常に脆弱です。Usernameプロパティを他のプロパティに変更すると、テストが失敗します。次に、DataAnnotationsに依存し、コントローラーが実行していることに対してテストする方がよいでしょう。したがって、新しいモデルは次のようになります。
public class LoginModel
{
[Required( ErrorMessage = "Username is required!" )]
public string Username { get; set; }
}
そして、コントローラーのアクションは次のように変更されます。
[HttpPost]
public ActionResult Login( LoginModel loginModel )
{
if ( !Model.IsValid() )
{
return View( loginModel );
}
LoginService.Login( loginModel );
}
問題は、DataAnnotationsを完全に認識しない単体テストにあるため、テストは失敗します。私の同僚は、本当にテストする必要があるのは、LoginServiceが呼び出されないことだと言いましたが、これをテストする方法がわかりません。彼は次のようにMoqを使用することを提案しました:
[TestMethod]
public void Login_Post_Blank_Username_Displays_Error()
{
var controller = GetHomeController();
var loginModel = new LoginModel
{
Username = null
};
loginServiceMock.Setup( x => x.Login( It.IsAny<LoginModel>() ) )
.Callback( () => Assert.Fail( "Should not call LoginService if Username is blank!" ) );
var result = controller.Login( loginModel );
loginServiceMock.Verify();
}
これについてどう思いますか?サービスメソッドが呼び出されなかったことをテストする正しい方法は何ですか?ユーザーフォームで不良データをテストする正しい方法はどうですか?