使用最小起订量对控制器进行单元测试.如何模拟httpcontext



我正试图通过使用Moq来测试我的帐户控制器,这是我所做的

控制器

   private readonly IWebSecurity _webSecurity;
    public AccountController(IWebSecurity webSecurity)
    {
        this._webSecurity = webSecurity;
    }
    public ActionResult Login(LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid && _webSecurity.login(model))
        {
            return RedirectToLocal(returnUrl);
        }
        // If we got this far, something failed, redisplay form
        ModelState.AddModelError("", "The user name or password provided is incorrect.");
        return View(model);
    }
    private ActionResult RedirectToLocal(string returnUrl)
    {
        if (Url.IsLocalUrl(returnUrl))
        {
            return Redirect(returnUrl);
        }
        else
        {
            return RedirectToAction("Index", "Home");
        }
    }

IWebSecurity

public interface IWebSecurity
{
    bool login(LoginModel model);
}

public class WebSecurity : IWebSecurity
{
    public bool login(LoginModel model)
    {
        return WebMatrix.WebData.WebSecurity.Login(model.UserName, model.Password, model.RememberMe);
    }
}

MyTestClass

[AfterScenario]
    public void OnAfterScenario() {
        mockRepository.VerifyAll();
    }
    LoginModel loginModel;
    AccountController _controller;
    #region Initializing Mock Repository
    readonly Mock<IWebSecurity> mockRepository = new Mock<IWebSecurity>(MockBehavior.Loose);
    ViewResult viewResult;
    #endregion
    [Given]
    public void Given_Account_controller()
    {
        _controller = new AccountController(mockRepository.Object);
    }
    [When]
    public void When_login_is_called_with_LoginModel(Table table)
    {
         loginModel = new LoginModel
            {
                UserName = table.Rows[0][1],
                Password = table.Rows[1][1]
            };
         mockRepository.Setup(x => x.login(loginModel)).Returns(true);
         viewResult = (ViewResult)_controller.Login(loginModel, "/");
    }
    [Then]
    public void Then_it_should_validate_LoginModel()
    {
       Assert.IsTrue(_controller.ModelState.IsValid);
    }
    [Then]
    public void Then_it_should_return_default_view()
    {
        Assert.AreEqual(viewResult.ViewName, "Index");
    }

但我的测试是失败的,它给出了期望,当它来到Redirect to Local方法的Url.IsLocal。我认为这里应该模拟我的httpcontextbase和httpcontextquestbase。

但我不知道如何嘲笑。

Thanks in advance

您应该模拟HttpContext。我编写了这个帮助器来做这些事情

public static Mock<HttpContextBase> MockControllerContext(bool authenticated, bool isAjaxRequest)
{
  var request = new Mock<HttpRequestBase>();
  request.SetupGet(r => r.HttpMethod).Returns("GET");
  request.SetupGet(r => r.IsAuthenticated).Returns(authenticated);
  request.SetupGet(r => r.ApplicationPath).Returns("/");
  request.SetupGet(r => r.ServerVariables).Returns((NameValueCollection)null);
  request.SetupGet(r => r.Url).Returns(new Uri("http://localhost/app", UriKind.Absolute));
  if (isAjaxRequest)
    request.SetupGet(x => x.Headers).Returns(new System.Net.WebHeaderCollection { { "X-Requested-With", "XMLHttpRequest" } });
  var server = new Mock<HttpServerUtilityBase>();
  server.Setup(x => x.MapPath(It.IsAny<string>())).Returns(BasePath);
  var response = new Mock<HttpResponseBase>();
  response.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>())).Returns((String url) => url);
  var session = new MockHttpSession();
  var mockHttpContext = new Mock<HttpContextBase>();
  mockHttpContext.Setup(c => c.Request).Returns(request.Object);
  mockHttpContext.Setup(c => c.Response).Returns(response.Object);
  mockHttpContext.Setup(c => c.Server).Returns(server.Object);
  mockHttpContext.Setup(x => x.Session).Returns(session);
  return mockHttpContext;
}
public class MockHttpSession : HttpSessionStateBase
{
  private readonly Dictionary<string, object> sessionStorage = new Dictionary<string, object>();
  public override object this[string name]
  {
    get { return sessionStorage.ContainsKey(name) ? sessionStorage[name] : null; }
    set { sessionStorage[name] = value; }
  }
  public override void Remove(string name)
  {
    sessionStorage.Remove(name);
  }
}

在测试方法中你可以这样使用

private AccountController GetController(bool authenticated)
{
  var requestContext = new RequestContext(Evoltel.BeniRosa.Web.Frontend.Tests.Utilities.MockControllerContext(authenticated, false).Object, new RouteData());
  var controller = new CofaniController(cofaniRepository.Object, categorieRepository.Object, emailService.Object, usersService.Object)
  {
    Url = new UrlHelper(requestContext)
  };
  controller.ControllerContext = new ControllerContext()
  {
    Controller = controller,
    RequestContext = requestContext
  };
  return controller;
}
[Test]
public void LogOn_Post_ReturnsRedirectOnSuccess_WithoutReturnUrl()
{
  AccountController controller = GetController(false);
  var httpContext = Utilities.MockControllerContext(false, false).Object;
  controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);
  LogOnModel model = new LogOnModel()
  {
    UserName = "someUser",
    Password = "goodPassword",
    RememberMe = false
  };
  ActionResult result = controller.LogOn(model, null);
  Assert.IsInstanceOf(typeof(RedirectToRouteResult), result);
  RedirectToRouteResult redirectResult = (RedirectToRouteResult)result;
  Assert.AreEqual("Home", redirectResult.RouteValues["controller"]);
  Assert.AreEqual("Index", redirectResult.RouteValues["action"]);
}

希望有所帮助

在这个特殊的问题中,你可以简单地用模拟的UrlHelper类覆盖控制器的Url属性。

对于HttpContext mock,最好将HttpContextBase注入到控制器并配置DI容器以为您提供适当的服务。这样便于以后在测试时模拟它。我相信Autofac有一些自动配置ASP容器的方法。如HttpContextBase .

编辑

似乎你不能用Moq模拟UrlHelper,正如@lazyberezovsky所写的那样——你只能模拟接口和虚拟方法。但这并不妨碍您编写自己的模拟对象。这是真的,你需要模拟HttpContext,因为它是UrlHelper构造函数所要求的(实际上,它是RequestContext构造函数所要求的,这是UrlHelper构造函数所要求的)…此外,IsLocalUrl不使用上下文中的任何内容,因此您不必提供任何额外的设置。

示例代码如下:

控制器

:

public ActionResult Foo(string url)
{
    if (Url.IsLocalUrl(url))
    {
        return Redirect(url);
    }
    return RedirectToAction("Index", "Home");
}
测试

:

[TestClass]
public class HomeControllerTests
{
    private Mock<HttpContextBase> _contextMock;
    private UrlHelper _urlHelperMock;
    public HomeControllerTests()
    {
        _contextMock = new Mock<HttpContextBase>();
        _urlHelperMock = new UrlHelper(new RequestContext(_contextMock.Object, new RouteData()));
    }

    [TestMethod]
    public void LocalUrlTest()
    {
        HomeController controller = new HomeController();
        controller.Url = _urlHelperMock;
        RedirectResult result = (RedirectResult)controller.Foo("/");
        Assert.AreEqual("/", result.Url);
    }
    [TestMethod]
    public void ExternalUrlTest()
    {
        HomeController controller = new HomeController();
        controller.Url = _urlHelperMock;
        RedirectToRouteResult result = (RedirectToRouteResult)controller.Foo("http://test.com/SomeUrl");
        Assert.AreEqual("Index", result.RouteValues["action"]);
        Assert.AreEqual("Home", result.RouteValues["controller"]);
    }
}

最新更新