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

发布于 2024-11-05 21:25:38 字数 1762 浏览 1 评论 0原文

尝试在我的 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

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

Trying to do some controller unit-testing in my ASP.NET MVC 3 web application.

My test goes like this:

[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");
}

Pretty simple. Basically testing a [HttpPost] action to ensure it returns a RedirectResult (PRG pattern). I'm not using RedirectToRouteResult because none of the overloads support anchor links. Moving on.

Now, i'm using Moq to mock the Http Context, including server variables, controller context, session, etc. All going well so far.

Until i've hit this line in my action method:

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

LandingPageWithAnchor is a custom HTML helper:

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);
}

Basically, i redirect to another page which is a "landing page" for new content, with an anchor on the new review. Cool.

Now, this method was failing before because UrlHelper was null.

So i did this in my mocking:

controller.Url = new UrlHelper(fakeRequestContext);

Which got it further, but now it's failing because the route tables don't contain a definition for "Landing_Page".

So i know i need to mock "something", but im not sure if it's:

a) The route tables

b) The UrlHelper.RouteUrl method

c) The UrlHelper.LandingPageWithAnchor extension method i wrote

Can anyone provide some guidance?

EDIT

This particular route is in an Area, so i tried calling the area registration in my unit test:

AreaRegistration.RegisterAllAreas();

But i get an InvalidOperationException:

This method cannot be called during the application's pre-start initialization stage.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

那片花海 2024-11-12 21:25:38

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

Got it working by mocking the HttpContext, RequestContext and ControllerContext, registering the routes then creating a UrlHelper with those routes.

Goes a little like this:

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() is a Moq helper which creates all the mock stuff, server variables, session, etc.

瀞厅☆埖开 2024-11-12 21:25:38

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);
}

There is a constructor for UrlHelper that takes a RouteCollection as a second argument. If you have the default setup that MVC creates for you, then I think this should work for you:

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

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

An alternative is to modify how your application starts up to make things a little easier to test and work with. If you define and interface like this:

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

With an implementation:

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

    // Other startup operations
}

Then you can modify your Global.asax like this:

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

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

And still have the flexibility to register your routes for testing. Something like this:

private IMvcApplication _app;
private RouteCollection _routes;

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

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

This can be similarly leveraged to work with area registrations as well.

private RouteCollection _routes;
private MyCustomAreaRegistration _area;

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

    _area.RegisterArea(context);
}
树深时见影 2024-11-12 21:25:38

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

为了解决这个问题,请按照 @ataddeini 的建议进行操作并创建一个路由集合。向这个集合添加一条新路线,它看起来像......

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

Because this is a test, your test suite is most likely not reading the Global.asax like you may be expecting.

To get around this, do what @ataddeini suggested and create a route collection. To this collection, add in a new route, which will look like...

var routes = new RouteCollection();
routes.Add(new Route("Landing_Page", new { /*pass route params*/}, null));
var helper = new UrlHelper(fakeRequestContext, routes);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文