在AreaRegistration.RegisterAllAreas()上单独测试带有区域的ASP.NET MVC 2路由

San*_*zen 20 .net unit-testing mstest visual-studio-2010 asp.net-mvc-2

我在ASP.NET MVC 2中测试我的路由.我正在使用MSTest,我也在使用区域.

[TestClass]
public class RouteRegistrarTests
{
    [ClassInitialize]
    public static void ClassInitialize(TestContext testContext)
    {
        RouteTable.Routes.Clear();

        RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        RouteTable.Routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });

        AreaRegistration.RegisterAllAreas();

        routes.MapRoute(
            "default",
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }

    [TestMethod]
    public void RouteMaps_VerifyMappings_Match()
    {
        "~/".Route().ShouldMapTo<HomeController>(n => n.Index());
    }
}
Run Code Online (Sandbox Code Playgroud)

AreaRegistration.RegisterAllAreas()但是,当它执行时,它会抛出此异常:

System.InvalidOperationException:System.InvalidOperationException:在应用程序的启动前初始化阶段,无法调用此方法.

所以,我认为我不能从我的类初始化程序中调用它.但我什么时候可以打电话呢?我的测试显然没有Application_Start.

Jas*_*tti 28

我通过创建我的AreaRegistration类的实例并调用该RegisterArea方法来解决这个问题.

例如,给定一个名为"Catalog"的区域,并使用此路径:

public override void RegisterArea(AreaRegistrationContext context)
{
  context.MapRoute(
      "Catalog_default",
      "Catalog/{controller}/{action}/{id}",
      new {controller = "List", action = "Index", id = "" }
  );
}
Run Code Online (Sandbox Code Playgroud)

这是我的测试方法:

[TestMethod]
public void TestCatalogAreaRoute()
{
  var routes = new RouteCollection();

  // Get my AreaRegistration class
  var areaRegistration = new CatalogAreaRegistration();
  Assert.AreEqual("Catalog", areaRegistration.AreaName);

  // Get an AreaRegistrationContext for my class. Give it an empty RouteCollection
  var areaRegistrationContext = new AreaRegistrationContext(areaRegistration.AreaName, routes);
  areaRegistration.RegisterArea(areaRegistrationContext);

  // Mock up an HttpContext object with my test path (using Moq)
  var context = new Mock<HttpContextBase>();
  context.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath).Returns("~/Catalog");

  // Get the RouteData based on the HttpContext
  var routeData = routes.GetRouteData(context.Object);

  Assert.IsNotNull(routeData, "Should have found the route");
  Assert.AreEqual("Catalog", routeData.DataTokens["area"]);
  Assert.AreEqual("List", routeData.Values["controller"]);
  Assert.AreEqual("Index", routeData.Values["action"]);
  Assert.AreEqual("", routeData.Values["id"]);
}
Run Code Online (Sandbox Code Playgroud)


Tod*_*ier 11

我知道我在这里迟到了,但我自己也解决了这个问题.与Jason类似的解决方案(一次注册一个区域),但是和我一样,我正在使用MvcContrib.TestHelper而不是自己做嘲弄.

[TestInitialize]
public void Setup() {
    RouteTable.Routes.Clear();
    var areaReg = new AdminAreaRegistration();
    areaReg.RegisterArea(new AreaRegistrationContext(areaReg.AreaName, RouteTable.Routes));
}

[TestMethod]
public void admin_should_map_to_home() {
    "~/Admin".ShouldMapTo<HomeController>(c => c.Index());
}
Run Code Online (Sandbox Code Playgroud)

请注意,MvcContrib对Rhino Mocks有很强的依赖性.虽然我更喜欢使用Moq,但我很高兴包含Rhino dll只是为了获得这个不错的功能.


er-*_*r-v 10

那么在测试项目中没有地方可以放置AreaRegistration.RegisterAllAreas(); 为了使它工作,因为它使用System.Web.Compilation.BuildManager类来编译网站的代码,如果在ASP.NET管道之外调用它会失败.我认为这是一种bug,因为它真的让测试很难运行.

但我发明了一个2步的解决方法:)

首先,您应该修改测试项目的App.Config文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>

    </appSettings>

    <connectionStrings>

    </connectionStrings>
    <system.web>
        <compilation debug="true">
            <assemblies>
                <add assembly="!!!NAME_OF_YOUR_MVC_WEB_ASSEMBLY!!!"/>       
            </assemblies>
        </compilation>
    </system.web>
    </configuration>
Run Code Online (Sandbox Code Playgroud)

Actualy你应该引用包含AreaRegistration下行程序的所有程序集.第二,在AreaRegistration.RegisterAllAreas()之前添加这个丑陋的代码;

typeof(BuildManager).GetProperty("PreStartInitStage", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, 2, null);

typeof(BuildManager).GetField("_topLevelFilesCompiledStarted", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(   typeof(BuildManager).GetField("_theBuildManager", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null), true);
Run Code Online (Sandbox Code Playgroud)

这仅适用于.Net 4.0及更高版本