ASP.NET MVC2 Custom View Engine игнорируется в модульных тестах - PullRequest
2 голосов
/ 10 декабря 2010

Я пытаюсь выполнить модульное тестирование контроллера, который вставляет данные в ViewData.Все наши представления требуют аналогичных данных (информация о клиенте, полученная из URL).Поэтому вместо того, чтобы помещать вызов в каждый отдельный метод контроллера, первоначальные разработчики решили поместить эту вставку ViewData в событие OnActionExecuting.

Конечно, когда вы вызываете действие контроллера из модульного теста, OnActionExecutingПожар.(Спасибо команде MVC!)

Итак, я попытался создать собственный движок представления и добавить его данные клиента в controllerContext при запросе представления.Это отлично работает в браузере, но мой viewEngine игнорируется, когда я запускаю этот тест.Никакое количество ViewEngines.Add (новый funkyViewEngine) не имеет никакого эффекта.

  [TestMethod()]
            public void LoginTest()
            {
                ViewEngines.Engines.Clear();
                ViewEngines.Engines.Add(new FunkyViewEngine());

                UserController target = new UserController();
                target.SetStructureMap();  <--sets us to use the test repo

                target.ControllerContext.HttpContext = MVCHelpers.FakeHttpContext("https://customersubdomain.ourdomain.com");  <--moq magic
                var actual = target.Login();
                Assert.IsTrue(actual.GetType().IsAssignableFrom(typeof(System.Web.Mvc.ViewResult)));
                var  vr = actual as ViewResult;
                Assert.IsTrue(vr.ViewData.Community() != null);  <--"Community" should be set by viewengine
                Assert.IsTrue(vr.ViewData.Community().Subdomain == "customersubdomain.ourdomain");
                Assert.IsTrue(vr.ViewData.Community().CanRegister);
            }

Есть ли здесь надежда?Как мне либо: 1) создать метод, который вызывается при выполнении контроллера ОБА в браузере и структуре модуля, либо 2) получить структуру модуля для вызова моего механизма представления.

Ответы [ 2 ]

2 голосов
/ 10 декабря 2010

Извините за ваше разочарование.Причина, по которой вы видите, что OnActionExecuting не вызывается при непосредственном вызове метода действия из модульного теста, заключается в том, что в MVC это не так.

Запрос выполняется через «конвейер», которыйчто касается этой области, состоит из ControllerActionInvoker.Этот класс отвечает за:

  1. Поиск метода действия
  2. Вызов фильтров действия 'OnActionExecuting метод ( примечание: ваш класс контроллера также является фильтром действия)
  3. Вызов самого метода действия
  4. Вызов фильтров действия 'метод OnActionExecuted
  5. Обработка результата (например, нахождение представления и его рендеринг)

В своем модульном тесте вы непосредственно вызываете шаг 3. и пропускаете все остальные шаги.В модульном тесте вы обязаны вызывать любой код установки, необходимый для работы вашего действия.

Однако это не означает, что вы должны писать модульные тесты, использующие ControllerActionInvoker для выполнения всего конвейера.,Мы (команда MVC) уже убедились, что все части работают вместе.

Вместо этого вы должны протестировать свой конкретный код приложения.В этом случае вы можете рассмотреть возможность использования следующих модульных тестов:

  1. Тест, который проверяет, что при некотором вызове Url OnActionExecuting на вашем контроллере правильный объект Customer помещается в ViewData
  2. Тест, который проверяет, что с учетом некоторого объекта Customer, присутствующего в ViewData, ваш метод действия возвращает соответствующий результат

Мое последнее замечание: вы должны сохранить функциональность в OnActionExecuting.Пользовательский механизм просмотра определенно не подходит для него.

0 голосов
/ 10 декабря 2010

Не тот ответ, который вы, вероятно, ищете, но я использую пользовательский MvcHandler для достижения той же цели (получение клиента по URL в мультитенантном приложении). ViewEngine не кажется мне хорошим местом для такой логики ...

Мой пользовательский обработчик выглядит примерно так:

public class AccountMvcHandler : MvcHandler
{
    public Account Account { get; private set; }

    protected override IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)
    {
        return base.BeginProcessRequest(httpContext, callback, state);
    }

    protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
    {
        string accountName = this.RequestContext.RouteData.GetRequiredString("account");
        Account = ServiceFactory.GetService<ISecurityService>().GetAccount(accountName);

        return base.BeginProcessRequest(httpContext, callback, state);
    }
}
...