Тестирование модуля контроллера ASP.NET MVC - проблема с расширением UrlHelper - PullRequest
15 голосов
/ 10 мая 2011

Попытка выполнить несколько контроллеров модульного тестирования в моем веб-приложении 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 был нулевым.

Итак, я сделал это в своей насмешке:

controller.Url = new UrlHelper(fakeRequestContext);

Что дало ему дальнейшее развитие,но теперь он терпит неудачу, потому что таблицы маршрутов не содержат определения для «Landing_Page».

Так что я знаю, что мне нужно высмеять «что-то», но я не уверен, что это:

a) Таблицы маршрутовб) метод UrlHelper.RouteUrlc) Метод расширения UrlHelper.LandingPageWithAnchor, который я написал

Может ли кто-нибудь дать некоторые рекомендации?

РЕДАКТИРОВАТЬ

Этот конкретный маршрут находится в Площадь , поэтому я попытался вызвать регистрацию области в моем модульном тесте:

AreaRegistration.RegisterAllAreas();

Но я получаю InvalidOperationException:

Этот метод не может быть вызван во времяПредварительная стадия инициализации приложения.

Ответы [ 3 ]

12 голосов
/ 24 августа 2011

Получил работу, посмеиваясь над 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, который создает все фиктивные вещи, переменные сервера, сеанс и т. Д.

0 голосов
/ 12 мая 2011

Поскольку это тест, ваш набор тестов, скорее всего, не читает Global.asax, как вы, возможно, ожидаете.

Чтобы обойти это, сделайте то, что предложил @ataddeini и создайте коллекцию маршрутов. К этой коллекции добавьте новый маршрут, который будет выглядеть как ...

var routes = new RouteCollection();
routes.Add(new Route("Landing_Page", new { /*pass route params*/}, null));
var helper = new UrlHelper(fakeRequestContext, routes);
0 голосов
/ 10 мая 2011

Существует конструктор для 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);
}
...