如何在ASP.NET MVC中使用单元测试来使用路由?

时间:2009-12-06 22:12:42

标签: asp.net-mvc unit-testing moq

我正在编写针对我的ASP.NET MVC应用程序的单元测试,特别是我正在测试我编写的HtmlHelper扩展方法。扩展方法中有一行:

var innerHtml = htmlHelper.ActionLink(text, action, controller, routeValues, null);

当我在单元测试中运行时,无论传入的操作或控制器如何,生成的URL的href都是空白的。

这是我的单元测试:

var page = CreateProductDataPage(); //returns ProductDataPage object
var htmlHelper = Http.CreateHtmlHelperWithMocks<ProductDataPage>(new ViewDataDictionary<ProductDataPage>(page), false);
var result = htmlHelper.ProductListingBreadcrumb(true, null, null);

这是CreateHtmlHelperWithMocks方法:

public static HtmlHelper<T> CreateHtmlHelperWithMocks<T>(ViewDataDictionary<T> viewData, bool isLoggedIn) where T : class
{
    var mockViewDataContainer = new Mock<IViewDataContainer>();
    mockViewDataContainer.SetupGet(v => v.ViewData).Returns(viewData);

    return new HtmlHelper<T>(GetViewContextMock(viewData, isLoggedIn).Object, mockViewDataContainer.Object);
}

最后,这是GetViewContextMock方法:

public static Mock<ViewContext> GetViewContextMock(ViewDataDictionary viewData, bool isLoggedIn)
{
    var mock = new Mock<ViewContext>();

    mock.SetupGet(v => v.HttpContext).Returns(GetHttpContextMock(isLoggedIn).Object);
    mock.SetupGet(v => v.Controller).Returns(new Mock<ControllerBase>().Object);
    mock.SetupGet(v => v.View).Returns(new Mock<IView>().Object);
    mock.SetupGet(v => v.ViewData).Returns(viewData);
    mock.SetupGet(v => v.TempData).Returns(new TempDataDictionary());
    mock.SetupGet(v => v.RouteData).Returns(new RouteData());

    return mock;
}

2 个答案:

答案 0 :(得分:4)

更新:想出来。 a $$多么痛苦。万一其他人试图这样做......

第一步是在创建模拟HtmlHelper时从global.asax添加Route Collection。

    public static HtmlHelper<T> CreateHtmlHelperWithMocks<T>(ViewDataDictionary<T> viewData, bool isLoggedIn) where T : class
    {
        var mockViewDataContainer = new Mock<IViewDataContainer>();
        mockViewDataContainer.SetupGet(v => v.ViewData).Returns(viewData);

        //These next two lines are key:
        var routeCollection = new RouteCollection();
        MvcApplication.RegisterRoutes(routeCollection);

        return new HtmlHelper<T>(GetViewContextMock(viewData, isLoggedIn).Object, mockViewDataContainer.Object, routeCollection);
    }

然后我必须确保HttpContext mock为Request的 ApplicationPath 属性和Response的 ApplyAppPathModifier 方法返回了一个结果。

    public static Mock<HttpContextBase> GetHttpContextMock(bool isLoggedIn)
    {
        var context = new Mock<HttpContextBase>();
        var request = new Mock<HttpRequestBase>();
        var response = new Mock<HttpResponseBase>();
        var session = new Mock<HttpSessionStateBase>();
        var server = new Mock<HttpServerUtilityBase>();
        var principal = AuthenticationAndAuthorization.GetPrincipleMock(isLoggedIn);

        //These next two lines are required for the routing to generate valid URLs, apparently:
        request.SetupGet(r => r.ApplicationPath).Returns("/");
        response.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>())).Returns((string r) => r);

        context.SetupGet(c => c.Request).Returns(request.Object);
        context.SetupGet(c => c.Response).Returns(response.Object);
        context.SetupGet(c => c.Session).Returns(session.Object);
        context.SetupGet(c => c.Server).Returns(server.Object);
        context.SetupGet(c => c.User).Returns(principal.Object);

        return context;
    }

答案 1 :(得分:0)

我在大约一个月前写过关于Rhino.Mocks这样做的博客。您可以在http://farm-fresh-code.blogspot.com/2009/10/mocking-htmlhelper-class-with.html找到有关我如何处理此问题的更多信息。基本上,我的解决方案是提供模拟中的所有内容,包括RouteData和通过连接到模拟助手的Response对象上的ApplyAppPathModifier。实际上它更像是基于底层存根的假帮手。