我想知道如何在从依赖于HttpContext
的基本控制器继承控制器时对其进行单元测试。下面是我继承的控制器,名为 BaseInterimController 。以下是我希望单元测试的AccountController
方法。我们正在使用MOQ。
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 :(得分: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
,这将允许更好的单元测试,因为您可以使用Moq轻松地模拟/伪造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
}