我试图理解MVC 4应用程序中TDD的概念。 我在网上找到的所有例子都没有向我解释在MVC 4应用程序的情况下应该测试单元测试的概念。
因此,当您编写单元测试时,您试图在控制器中测试的要点是什么?
如果可能,请解释一下这个例子。
public class UsersIDentController : AuthorizedController
{
private readonly IUserDetailsService userDetailsService;
public UsersIDentController(IUserDetailsService userDetailsService,
IServiceLocator serviceLocator): base(serviceLocator)
{
}
//
// GET: /UsersIdentification/
[AllowAnonymous]
public ActionResult ShowDetails()
{
UsersIdentificationViewModel model = new UsersIdentificationViewModel();
return View(model);
}
}
如果你要为这个控制器(gettin用户数据)编写单元测试,你将在单元测试中测试什么。
感谢。
答案 0 :(得分:7)
我不能详细说明你当前的例子中的单元测试。因为你所得到的只有一个方法ShowDetails
,它返回与View
绑定的UsersIdentificationViewModel
。由于您只返回View
,因此您可以将返回类型更改为ViewResult
。然后,您可以测试的是,与View
绑定的模型是否为UsersIdentificationViewModel
类型。
如果我们举一个简单的例子,我可以更好地解释MVC中的单元测试。如果您创建一个新的MVC应用程序,选择internet template
,您会看到AccountController
被定义为默认值,它包含 login , register 的操作, 更改密码等。
我们在LogOn
。
AccountController
操作
<强> AccountController.cs 强>
public class AccountController : Controller
{
private readonly IAuthProvider _authProvider;
public AccountController(IAuthProvider authProvider)
{
_authProvider = authProvider;
}
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (_authProvider.ValidateUser(model.UserName, model.Password))
{
_authProvider.SetCookie(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
return View(model);
}
...
}
为了避免FormsAuthentication
使用AccountController
密封类的依赖,我使用了接口IAuthProvider
来简化单元测试。
<强> IAuthProvider.cs 强>
public interface IAuthProvider
{
bool ValidateUser(string username, string password);
void SetCookie(string username, bool rememberMe);
bool CreateUser(string username, string password, string email, out string error);
bool ChangePassword(string username, string oldPassword, string newPassword);
void SignOut();
}
<强> LogOnModel.cs 强>
public class LogOnModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
您可以在LogOn
操作中注意到许多 if..else 条件,它们是单元测试的良好候选者。
我会为行动写下至少四个单元测试。
returnUrl
的情况下传递有效凭据AccountController
应将用户重定向到Home
操作。以下是我使用MSTest
和RhinoMocks
编写的单元测试。
<强> AccountControllerTests.cs 强>
[TestClass]
public class AccountControllerTests
{
private AccountController _accountController;
private IAuthProvider _mockAuthProvider;
[TestInitialize]
public void SetUp()
{
//** Arrange
_mockAuthProvider = MockRepository.GenerateStub<IAuthProvider>();
_accountController = new AccountController(_mockAuthProvider);
}
[TestCleanup]
public void CleanUp()
{
}
/// <summary>
/// This test is to verify on entering valid credentials the action should redirect to the passed url.
/// </summary>
[TestMethod]
public void LogOn_Action_Valid_Credentials_With_ReturnUrl_Test()
{
//** Arrange
var logonModel = new LogOnModel
{
UserName = "trigent",
Password = "password",
RememberMe = true
};
// stub the ValidateUser to return "true" to pretend the user is valid.
_mockAuthProvider.Stub(x => x.ValidateUser(logonModel.UserName, logonModel.Password)).Return(true);
//** Act
var actual = _accountController.LogOn(logonModel, "/");
//** Assert
// verify RedirectResult is returned from action
Assert.IsInstanceOfType(actual, typeof(RedirectResult));
// verify the redirect url is same as the passed one.
Assert.AreEqual(((RedirectResult)actual).Url, "/");
}
/// <summary>
/// This test is to verify on passing valid credentials without returnUrl the account controller
/// should redirect the user to the "Home" action.
/// </summary>
[TestMethod]
public void LogOn_Action_Valid_Credentials_Without_ReturnUrl_Test()
{
//** Arrange
var logonModel = new LogOnModel
{
UserName = "trigent",
Password = "password",
RememberMe = true
};
// stub the ValidateUser to return "true" to pretend the user is valid.
_mockAuthProvider.Stub(x => x.ValidateUser(logonModel.UserName, logonModel.Password)).Return(true);
//** Act
var actual = _accountController.LogOn(logonModel, string.Empty);
//** Assert
// verify RedirectToRouteResult is returned from action
Assert.IsInstanceOfType(actual, typeof(RedirectToRouteResult));
// verify the controller redirecting to "Home" action.
var routeValues = ((RedirectToRouteResult)actual).RouteValues;
Assert.AreEqual("Home", routeValues["controller"].ToString());
Assert.AreEqual("Index", routeValues["action"].ToString());
}
/// <summary>
/// This test is to verify on passing invalid credentials the account controller should return the login view
/// with error messages.
/// </summary>
[TestMethod]
public void LogOn_Action_Invalid_Credentials_Test()
{
//** Arrange
var logonModel = new LogOnModel
{
UserName = "trigent",
Password = "password",
RememberMe = true
};
// stub the ValidateUser to return "false" to pretend the user is invalid.
_mockAuthProvider.Stub(x => x.ValidateUser(logonModel.UserName, logonModel.Password)).Return(false);
//** Act
var actual = _accountController.LogOn(logonModel, string.Empty);
//** Assert
// verify ViewResult is returned from action
Assert.IsInstanceOfType(actual, typeof(ViewResult));
// verify the controller throws error.
var modelStateErrors = _accountController.ModelState[""].Errors;
Assert.IsTrue(modelStateErrors.Count > 0);
Assert.AreEqual("The user name or password provided is incorrect.", modelStateErrors[0].ErrorMessage);
}
/// <summary>
/// This test is to verify when there is a validation error the controller should return the same login view.
/// </summary>
[TestMethod]
public void LogOn_Action_Invalid_Input_Test()
{
//** Arrange
_accountController.ModelState.AddModelError("UserName", "UserName is Required.");
//** Act
var actual = _accountController.LogOn(new LogOnModel(), string.Empty);
//** Assert
// verify ViewResult is returned from action
Assert.IsInstanceOfType(actual, typeof(ViewResult));
}
}
答案 1 :(得分:1)
如果您测试ShowDetails方法,您可以验证它是否返回具有正确类型的模型的视图。
为此,您可以使用MVC Contrib,它可以使用单元测试断言。您可以在此处找到所有单元测试文档:http://mvccontrib.codeplex.com/wikipage?title=TestHelper&referringTitle=Documentation