2012-11-13 121 views
3

我想了解在MVC 4應用程序中TDD的概念。 我在網上找到的所有例子都沒有向我解釋MVC 4應用程序的單元測試應該測試的概念。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); 
    } 
} 

如果您要爲此控制器編寫單元測試(請參閱用戶數據),您將在單元測試中測試什麼?

謝謝。

回答

7

我不能在當前的例子中詳細說明單元測試。因爲你所得到的只有一個方法ShowDetails,它會返回ViewUsersIdentificationViewModel並列。由於您僅返回View,因此您可以將退貨類型更改爲ViewResult。然後,您可以測試的是與View綁定的型號是否爲UsersIdentificationViewModel

如果我們舉一個簡單的例子,我可以更好地解釋MVC中的單元測試。如果您創建一個新的MVC應用程序選擇internet template你會看到一個AccountController被定義爲默認值,它包含登錄寄存器修改密碼的操作

讓我們以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); 
    } 

    ... 
} 

爲了避免與FormsAuthenticationAccountController密封類的依賴我已經使用的接口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條件,他們是單位好candiates測試。

我會爲這個動作寫至少四個單元測試。

  1. 驗證輸入有效憑證的動作應該重定向到傳遞的URL。
  2. 驗證通過有效憑證沒有returnUrlAccountController應該將用戶重定向到Home操作。
  3. 驗證傳遞無效憑證時,帳戶控制器應返回錯誤視圖。
  4. 驗證何時出現驗證錯誤,控制器應返回錯誤視圖。

下面是我用MSTestRhinoMocks編寫的單元測試。

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)); 
    } 
}