Тестирование блока контроллера ASP.NET MVC - проблема с расширением UrlHelper
Попытка выполнить блок-тестирование контроллера в моем веб-приложении ASP.NET MVC 3.
Мой тест выглядит следующим образом:
[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, который я написал
Может ли кто-нибудь дать некоторые рекомендации?
ИЗМЕНИТЬ
Этот конкретный маршрут находится в Области, поэтому я попытался вызвать регистрацию области в моем unit test:
AreaRegistration.RegisterAllAreas();
Но я получаю InvalidOperationException
:
Этот метод не может быть вызван во время этапа инициализации перед запуском приложения.
Ответы
Ответ 1
Получил работу, издеваясь над 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, который создает все макеты, серверные переменные, сеанс и т.д.
Ответ 2
Существует конструктор для 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);
}
Ответ 3
Поскольку это тест, ваш тестовый пакет, скорее всего, не читает Global.asax, как вы ожидаете.
Чтобы обойти это, сделайте то, что предложил @ataddeini, и создайте коллекцию маршрутов. В эту коллекцию добавьте новый маршрут, который будет выглядеть как...
var routes = new RouteCollection();
routes.Add(new Route("Landing_Page", new { /*pass route params*/}, null));
var helper = new UrlHelper(fakeRequestContext, routes);