[英]How to test that a service method was not called
我和我的同事正在讨论编写单元测试的正确方法,以确保当在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 );
}
他向我指出,这实际上不是针对这种情况编写测试的正确方法。 由于一个原因,它非常脆弱-将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();
}
您对此有何想法? 测试未调用服务方法的正确方法是什么? 如何测试用户表单中的不良数据的正确方法呢?
验证时:
loginServiceMock.Verify( x => x.Login( It.IsAny<LoginModel>() ), Times.Never() );
您应该在模拟中删除设置。 为了使代码在操作中输入if语句,可以在调用控制器中的操作之前在ModelState中添加模型错误。
您的代码应如下所示:
[TestMethod]
public void Login_Post_Blank_Username_Displays_Error()
{
var controller = GetHomeController();
var loginModel = new LoginModel
{
Username = null
};
controller.ModelState.AddModelError("a key", "a value");
var result = controller.Login( loginModel );
loginServiceMock.Verify( x => x.Login( It.IsAny<LoginModel>() ), Times.Never() );
}
假设已通过属性注入设置了Mocked LoginService,则可以编写以下测试。
[TestMethod]
public void LoginPost_WhenUserNameIsNull_VerifyLoginMethodHasNotBeenCalled()
{
//Arrange
var loginServiceMock = new Mock<ILoginService>();
var sut = new HomeController { LoginService = loginServiceMock .Object};
var loginModel = new LoginModel {
Username = null
};
sut.ModelState.AddModelError("fakeKey", "fakeValue");
//Act
sut.Login(loginModel);
//Verify
loginServiceMock.Verify(x => x.Login(loginModel), Times.Never(), "Login method has been called.");
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.