为什么在UnitTesting期间Url.Action返回null?

时间:2016-06-15 10:30:34

标签: asp.net-mvc unit-testing url mocking

我正在尝试进行单元测试,其中还包括Moq版本4.5.9。 简单地说,我只想像在Mvc控制器中那样获得Url.Action。 这就是我所做的:

var Cntrlr = new ErrorController();
var HttpCnt = new Mock<HttpContextHelper>(Utility.FakeHttpContext);
var UrlHelperMock = new Mock<UrlHelper>(HttpCnt.Object.ContextRequest.RequestContext);

Cntrlr.Url = UrlHelperMock.Object;
var RedirectUrl = Cntrlr.Url.Action("Index", "Error", new { errorName = "NotSupport" });

另外,如果你必须知道,FakeHttpContext是这样的:

public static HttpContext FakeHttpContext
{
    get
    {
        var HttpRequest = new HttpRequest("", "http://localhost/", ""); 
        var StringWriter = new StringWriter();
        var HttpResponse = new HttpResponse(StringWriter);
        var HttpContext = new HttpContext(HttpRequest, HttpResponse);
        var SessionContainer = new HttpSessionStateContainer("id", 
            new SessionStateItemCollection(),
            new HttpStaticObjectsCollection(), 10, true,
            HttpCookieMode.AutoDetect, 
            SessionStateMode.InProc, false);
        HttpContext.Items["AspSession"] = typeof(HttpSessionState)
            .GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
                null, CallingConventions.Standard,
                new[] { typeof(HttpSessionStateContainer) }, null)
            .Invoke(new object[] { SessionContainer });
        return HttpContext;
    }
}

为什么RedirectUrl会返回Null?你能帮忙吗?

2 个答案:

答案 0 :(得分:1)

Url.Action在这种情况下返回null的原因是因为您已经模拟了UrlHelper,但是您还没有定义当您的代码调用{{1}时应该发生的事情你的模拟方法。

在MVC 5中模拟UrlHelper.Action

Url.Action

我们的想法是,您应该在特定条件下测试您的代码,而不关心您是在幕后调用真实的还是假的var Cntrlr = new ErrorController(); var UrlHelperMock = new Mock<UrlHelper>(); UrlHelperMock .Setup(x => x.Action(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<object>())) .Returns("/Error/Index/NotSupport"); Cntrlr.Url = UrlHelperMock.Object; var RedirectUrl = Cntrlr.Url.Action("Index", "Error", new { errorName = "NotSupport" }); 。您应该只模拟最低限度以使测试工作,因此在这种情况下创建假的Url.ActionHttpContext是过度的。

  

注意:您应该假设MVC框架已经有测试以确保RequestContext功能。而且,您应该unit test your routes确保Url.Action在所有情况下都会返回正确的网址。但这是单元测试的另一个问题。

答案 1 :(得分:0)

我找到了答案here

public static class AutoMockContainer
{
    public static AutoMock Create()
    {
        var autoMock = new AutoMock();

        var httpContext = Substitute.For<HttpContextBase>();
        autoMock.Provide(httpContext);

        var server = Substitute.For<HttpServerUtilityBase>();
        httpContext.Server.Returns(server);

        var request = Substitute.For<HttpRequestBase>();
        var parameters = new NameValueCollection();
        request.Params.Returns(parameters);
        var formParameters = new NameValueCollection();
        request.Form.Returns(formParameters);
        var qsParameters = new NameValueCollection();
        request.QueryString.Returns(qsParameters);
        var headers = new NameValueCollection();
        headers.Add("Host", "localhost");
        request.Headers.Returns(headers);
        request.AppRelativeCurrentExecutionFilePath.Returns("~/");
        request.ApplicationPath.Returns("/");
        request.Url.Returns(new Uri("http://localhost/"));
        request.Cookies.Returns(new HttpCookieCollection());
        request.ServerVariables.Returns(new NameValueCollection());
        autoMock.Provide(request);
        httpContext.Request.Returns(request);

        var response = Substitute.For<HttpResponseBase>();
        response.Cookies.Returns(new HttpCookieCollection());
        response.ApplyAppPathModifier(Arg.Any<string>()).Returns(a => a.Arg<string>());
        autoMock.Provide(response);
        httpContext.Response.Returns(response);

        var routeData = new RouteData();

        var requestContext = Substitute.For<RequestContext>();
        requestContext.RouteData = routeData;
        requestContext.HttpContext = httpContext;
        autoMock.Provide(requestContext);

        var actionExecutingContext = Substitute.For<ActionExecutingContext>();
        actionExecutingContext.HttpContext.Returns(httpContext);
        actionExecutingContext.RouteData.Returns(routeData);
        actionExecutingContext.RequestContext = requestContext;
        autoMock.Provide(actionExecutingContext);

        var actionExecutedContext = Substitute.For<ActionExecutedContext>();
        actionExecutedContext.HttpContext.Returns(httpContext);
        actionExecutedContext.RouteData.Returns(routeData);
        actionExecutedContext.RequestContext = requestContext;
        autoMock.Provide(actionExecutedContext);

        var controller = Substitute.For<ControllerBase>();
        autoMock.Provide(controller);
        actionExecutingContext.Controller.Returns(controller);

        var controllerContext = Substitute.For<ControllerContext>();
        controllerContext.HttpContext = httpContext;
        controllerContext.RouteData = routeData;
        controllerContext.RequestContext = requestContext;
        controllerContext.Controller = controller;
        autoMock.Provide(controllerContext);
        controller.ControllerContext = controllerContext;

        var iView = Substitute.For<IView>();
        autoMock.Provide(iView);

        var viewDataDictionary = new ViewDataDictionary();
        autoMock.Provide(viewDataDictionary);

        var iViewDataContainer = Substitute.For<IViewDataContainer>();
        iViewDataContainer.ViewData.Returns(viewDataDictionary);
        autoMock.Provide(iViewDataContainer);

        var textWriter = Substitute.For<TextWriter>();
        autoMock.Provide(textWriter);

        var viewContext = new ViewContext(controllerContext, iView, viewDataDictionary, new TempDataDictionary(), textWriter)
        {
            HttpContext = httpContext,
            RouteData = routeData,
            RequestContext = requestContext,
            Controller = controller
        };
        autoMock.Provide(viewContext);

        return autoMock;
    }

    public static T GetController<T>(this AutoMock autoMock) where T : Controller
    {
        var routes = new RouteCollection();
        MyApplication.RegisterRoutes(routes);
        var controller = autoMock.Resolve<T>();
        controller.ControllerContext = autoMock.Resolve<ControllerContext>();
        controller.Url = new UrlHelper(autoMock.Resolve<RequestContext>(), routes);
        return controller;
    }
}

初始化

var autoMock = AutoMockContainer.Create();
var controller = autoMock.GetController<HomeController>();