ASP.NET MVC控制器单元测试 - UrlHelper扩展问题

时间:2011-05-09 23:56:16

标签: c# asp.net-mvc unit-testing moq asp.net-mvc-routing

尝试在我的ASP.NET MVC 3 Web应用程序中执行一些控制器单元测试。

我的测试是这样的:

[TestMethod]
public void Ensure_CreateReviewHttpPostAction_RedirectsAppropriately()
{
   // Arrange.
   var newReview = CreateMockReview();

   // Act.
   var result = _controller.Create(newReview) as RedirectResult;

   // Assert.
   Assert.IsNotNull(result, "RedirectResult was not returned");
}

非常简单。基本上测试[HttpPost]操作以确保它返回RedirectResult(PRG模式)。我没有使用RedirectToRouteResult,因为没有任何重载支持锚链接。继续前进。

现在,我正在使用 Moq 来模拟Http Context,包括服务器变量,控制器上下文,会话等。到目前为止一切顺利。

直到我在我的动作方法中点击这一行:

return Redirect(Url.LandingPageWithAnchor(someObject.Uri, review.Uri);

LandingPageWithAnchor是一个自定义HTML帮助器:

public static string LandingPageWithAnchor(this UrlHelper helper, string uri1, string uri2)
{
   const string urlFormat = "{0}#{1}";

   return string.Format(urlFormat,
                helper.RouteUrl("Landing_Page", new { uri = uri1}),
                uri2);
}

基本上,我会重定向到另一个页面,该页面是新内容的“登录页面”,并在新评论中添加了一个锚点。凉。

现在,此方法失败,因为UrlHelper为空。

所以我在嘲笑中这样做了:

controller.Url = new UrlHelper(fakeRequestContext);

更进一步,但现在它失败了,因为路由表不包含“Landing_Page”的定义。

所以我知道我需要嘲笑“某事”,但我不确定是不是:

a)路线表
b)UrlHelper.RouteUrl方法
c)我写的UrlHelper.LandingPageWithAnchor扩展方法

任何人都可以提供一些指导吗?

修改

此特定路线位于区域,因此我尝试在单元测试中调用区域注册:

AreaRegistration.RegisterAllAreas();

但我得到InvalidOperationException

  

在应用程序的预启动初始化阶段,无法调用此方法。

3 个答案:

答案 0 :(得分:12)

通过模拟HttpContext,RequestContext和ControllerContext来工作,注册路由然后用这些路由创建UrlHelper

有点像这样:

public static void SetFakeControllerContext(this Controller controller, HttpContextBase httpContextBase)
{
    var httpContext = httpContextBase ?? FakeHttpContext().Object;
    var requestContext = new RequestContext(httpContext, new RouteData());
    var controllerContext = new ControllerContext(requestContext, controller);
    MvcApplication.RegisterRoutes();
    controller.ControllerContext = controllerContext;
    controller.Url = new UrlHelper(requestContext, RouteTable.Routes);
}

FakeHttpContext()是一个Moq助手,可以创建所有模拟内容,服务器变量,会话等。

答案 1 :(得分:0)

UrlHelper constructorRouteCollection作为第二个参数。如果你有MVC为你创建的默认设置,那么我认为这应该适合你:

var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);

controller.Url = new UrlHelper(fakeRequestContext, routes);

另一种方法是修改应用程序的启动方式,使事情更容易测试和使用。如果你这样定义和界面:

public interface IMvcApplication
{
    void RegisterRoutes(RouteCollection routes);
    // Other startup operations    
}

实施:

public class MyCustomApplication : IMvcApplication
{
    public void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        // Your route registrations here
    }

    // Other startup operations
}

然后您可以像这样修改Global.asax

public class MvcApplication : HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        var app = new MyCustomApplication();
        app.RegisterRoutes(RouteTable.Routes);
        // Other startup calls
    }
}

并且仍然可以灵活地注册您的路线进行测试。像这样:

private IMvcApplication _app;
private RouteCollection _routes;

[TestInitialize]
public void InitializeTests()
{
    _app = new MyCustomApplication();

    _routes = new RouteCollection();
    _app.RegisterRoutes(_routes);
}

这也可以类似地用于区域注册。

private RouteCollection _routes;
private MyCustomAreaRegistration _area;

[TestInitialize]
public void InitTests()
{
    _routes = new RouteCollection();
    var context = new AreaRegistrationContext("MyCustomArea", _routes);

    _area.RegisterArea(context);
}

答案 2 :(得分:0)

因为这是一个测试,所以您的测试套件很可能不会像您期望的那样阅读Global.asax。

要解决此问题,请执行@ataddeini建议并创建路径集合。对于此集合,添加一个新的路线,它看起来像......

var routes = new RouteCollection();
routes.Add(new Route("Landing_Page", new { /*pass route params*/}, null));
var helper = new UrlHelper(fakeRequestContext, routes);