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

本文关键字:UrlHelper 扩展 问题 单元测试 NET MVC 控制器 ASP | 更新日期: 2023-09-27 17:55:35

尝试在我的 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 上下文,包括服务器变量、控制器上下文、会话等。到目前为止一切顺利。

直到我在操作方法中达到这一行:

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 为 null。

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

controller.Url = new UrlHelper(fakeRequestContext);

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

所以我知道我需要嘲笑"某事",但我不确定它是否是:

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

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

编辑

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

AreaRegistration.RegisterAllAreas();

但我得到一个InvalidOperationException

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

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

通过模拟 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助手,可以创建所有模拟内容,服务器变量,会话等。

UrlHelper有一个构造函数,它将RouteCollection作为第二个参数。 如果您有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);
}

因为这是一个测试,所以你的测试套件很可能没有像你期望的那样读取 Global.asax。

若要解决此问题,请按照@ataddeini建议的操作并创建路由集合。 在此集合中添加一个新路由,该路由将如下所示...

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