Я - поблочное тестирование мои маршруты в 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());
}
}
Когда это выполняется AreaRegistration.RegisterAllAreas()
однако, это выдает это исключение:
Система. InvalidOperationException: Система. InvalidOperationException: Этот метод нельзя назвать во время этапа инициализации приложения перед запуском.
Так, я считаю, что не могу назвать его от своего инициализатора класса. Но когда я могу назвать его? Я, очевидно, не имею Application_Start
в моем тесте.
Я решил эту проблему, создав экземпляр моего класса AreaRegistration
и вызвав метод RegisterArea
.
Например, дана Область с именем «Каталог» с этим маршрутом:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Catalog_default",
"Catalog/{controller}/{action}/{id}",
new {controller = "List", action = "Index", id = "" }
);
}
Это мой метод тестирования:
[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"]);
}
Я думаю, вы ищете класс TestHelper в библиотеке MVC Contrib. Взгляните на тесты в MVC Contrib (он там спрятан). Вы обнаружите, что все красиво имитировано. H
MVCContrib.UnitTests \ TestHelper \ RoutesTest.cs - необходимо обновить вики! Удачи
using System.Web.Mvc;
using System.Web.Routing;
using NUnit.Framework;
namespace MVCContrib.Application.UnitTests.TestHelper
{
/// <summary>
/// Summary description for UserRoutesTest
/// </summary>
[TestFixture]
public class UserRoutesTest
{
[TestFixtureSetUp]
public void Setup()
{
var routes = RouteTable.Routes;
routes.Clear();
routes.MapRoute(
"Default", // Route name
"{controller}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
[Test]
public void homeIndex()
{
"~/user"
.ShouldMapTo<HomeController>(action => action.Index());
}
[Test]
public void HomeShow()
{
"~/home"
.GivenIncomingAs(HttpVerbs.Put)
.ShouldMapTo<HomeController>(action => action.Index());
}
}
}
Ну нет места в тестовом проекте, где можно поставить AreaRegistration.RegisterAllAreas (); чтобы заставить его работать, потому что он использует класс System.Web.Compilation.BuildManager для компиляции кода для веб-сайта и не работает, если он вызывается вне конвейера ASP.NET. Я думаю, что это своего рода ошибка, потому что она действительно затрудняет выполнение тестов.
Но я изобрел двухэтапный обходной путь :)
Сначала вы должны изменить файл 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>
На самом деле вы должны ссылаться на все сборки, которые содержат нисходящие элементы 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);
Это работает только для .Net 4.0 и выше
Я знаю, что вступаю здесь с опозданием, но я только что сам решал эту проблему. Решение аналогично Джейсону (регистрировать по одной области за раз), но, как и вы, я использую MvcContrib.TestHelper вместо того, чтобы делать свой собственный mocking.
[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());
}
Обратите внимание, что MvcContrib имеет жесткую зависимость от Rhino Mocks. Хотя я предпочитаю использовать Moq, я не против включить Rhino dll только для того, чтобы получить эту приятную функциональность.