2015-09-21 67 views
0

我想知道如何在從基於HttpContext的基本控制器繼承時對單元進行單元測試。下面是我的繼承控制器,名爲BaseInterimController。下面是我想單元測試的AccountController方法。我們正在使用最小起訂量。如何單元測試依賴於HttpContext的控制器

public abstract class BaseInterimController : Controller 
{ 

    #region Properties 
    protected string InterimName 
    { 
     get { return MultiInterim.GetInterimName(InterimIdentifier); } 
    } 

    internal virtual string InterimIdentifier 
    { 
     get { return System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values["InterimIdentifier"].ToString(); } 
    } 
} 

public class AccountController : BaseInterimController 
{ 
    [HttpPost] 
    [AllowAnonymous] 
    [ValidateInput(false)] 
    [Route(@"{InterimIdentifier:regex([a-z]{7}\d{4})}/Account/Signin")] 
    public ActionResult Signin(LoginViewModel model) 
    { 
     if (ModelState.IsValid) 
     { 
      var identity = Authentication.SignIn(model.Username, 
       model.Password) as LegIdentity; 

      if (identity != null && identity.IsAuthenticated) 
      { 
       return Redirect(model.ReturnUrl); 
      } 
      else 
      { 
       // Sign in failed 
       ModelState.AddModelError("", 
        Authentication.ExternalSignInFailedMessage); 
      } 
     } 
     return View(model); 
    } 
} 

回答

0

耦合的控制器HttpContext可以使你的代碼很難在單元測試HttpContext爲空,除非你試圖嘲笑它,因爲測試;你不應該這樣做。不要嘲笑你不擁有的代碼。

請嘗試將您想要從HttpContext獲得的功能抽象爲您可以控制的功能。

這只是一個例子。如果需要,您可以嘗試使其更通用。我將專注於您的具體情況。

你在你的控制器

System.Web.HttpContext.Current.Request 
    .RequestContext.RouteData.Values["InterimIdentifier"].ToString(); 

直接調用此當你真正追求的是什麼來獲取InterimIdentifier價值的能力。像

public interface IInterimIdentityProvider { 
     string InterimIdentifier { get; } 
} 

public class ConcreteInterimIdentityProvider : IInterimIdentityProvider { 
    public virtual string InterimIdentifier { 
     get { return System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values["InterimIdentifier"].ToString(); } 
    } 
} 

它可以稍後在具體的類中實現並注入到您的控制器提供您正在使用依賴注入。

那麼你基本控制器會像

public abstract class BaseInterimController : Controller { 
    protected IInterimIdentityProvider identifier; 
    public BaseInterimController(IInterimIdentityProvider identifier) { 
     this.identifier = identifier; 
    } 

    protected string InterimName { 
     get { return MultiInterim.GetInterimName(identifier.InterimIdentifier); } 
    } 

    //This can be refactored to the code above or use what you had before 
    //internal virtual string InterimIdentifier { 
    // get { return identifier.InterimIdentifier; } 
    //} 
}  

public class AccountController : BaseInterimController 
{ 
    public AccountController(IInterimIdentityProvider identifier) 
     : base(identifier){ } 

    [HttpPost] 
    [AllowAnonymous] 
    [ValidateInput(false)] 
    [Route(@"{InterimIdentifier:regex([a-z]{7}\d{4})}/Account/Signin")] 
    public ActionResult Signin(LoginViewModel model) 
    { 
     if (ModelState.IsValid) 
     { 
      var identity = Authentication.SignIn(model.Username, 
       model.Password) as LegIdentity; 

      if (identity != null && identity.IsAuthenticated) 
      { 
       return Redirect(model.ReturnUrl); 
      } 
      else 
      { 
       // Sign in failed 
       ModelState.AddModelError("", 
        Authentication.ExternalSignInFailedMessage); 
      } 
     } 
     return View(model); 
    } 
} 

這使得實現的控件不依賴於HttpContext這將允許更好的單元測試,你可以很容易地模擬/假IInterimIdentityProvider接口,使用起訂量返回你想在測試過程中。

[TestMethod] 
public void Account_Controller_Should_Signin() { 
    //Arrange 
    var mock = new Mock<IInterimIdentityProvider>(); 
    mock.Setup(m => m.InterimIdentifier).Returns("My identifier string"); 
    var controller = new AccountController(mock.Object); 
    var model = new LoginViewModel() { 
     Username = "TestUser", 
     Password = ""TestPassword 
    }; 
    //Act 
    var actionResult = controller.Signin(model); 
    //Assert 
    //...assert your expected results 
} 
相關問題