Добавьте ключи и значения в RouteData при использовании MVCContrib для модульного тестирования контроллеров MVC 3 и представлений

Итак, я использую MVCContrib TestHelper для модульного тестирования моих контроллеров, что прекрасно работает.

Однако, как и многие другие, под модульным тестом я на самом деле имею в виду интеграционный тест и хочу, по крайней мере, убедиться, что мои представления отображаются без ошибок, учитывая предоставленную модель... в противном случае я могу пропустить целый класс ошибок, в основном связанных с моделью, даже хотя я тестирую контроллер (например, представление не рендерится, если свойство модели имеет значение null).

Во всяком случае, я начал пытаться выяснить, как это сделать (иначе поискать, как это сделать). Казалось, что самым простым способом было создать HTMLHelper и сделать так, чтобы он просто отображал представления (в данном случае частичные).

К сожалению, когда я пытаюсь использовать мой поддельный HTMLHelper, он жалуется, что в данных маршрута нет имени контроллера.

Конечно же, я смотрю и контроллеры RouteData не заполнены. К сожалению, RouteData.Values ​​RouteValueDictionary доступен только для чтения, поэтому я не могу просто предоставить необходимые значения.

Я не женат на идее HTMLHelper, чтобы решить проблему фактического рендеринга представления как части теста, поэтому не стесняйтесь предлагать альтернативы там, но, пожалуйста, не беспокойтесь, предлагая мне протестировать свои представления с использованием Selenium, Watin или другого пользовательского интерфейса инструменты тестирования... Я хочу, чтобы элемент управления мог выполнять такие действия, как манипулирование и восстановление состояния и информации о данных для некоторых тестов, чего я не могу сделать с тестированием на основе пользовательского интерфейса.

Вот код, который я сейчас использую, чтобы попытаться визуализировать частичное:

    public class FakeView : IView
{
    #region IView Members
    public void Render(ViewContext viewContext, System.IO.TextWriter writer)
    {
        throw new NotImplementedException();
    }
    #endregion
}

public class WebTestUtilities
{
    public static void prepareCache()
    {
        SeedDataManager seed = new SeedDataManager();
        seed.CheckSeedDataStatus();
    }

    public static string RenderRazorViewToString(string viewName, object model, Controller controller)
    {
        var sb = new StringBuilder();
        var memWriter = new StringWriter(sb);
        var html = new HtmlHelper(new ViewContext(controller.ControllerContext,
            new FakeView(), new ViewDataDictionary(), new TempDataDictionary(), memWriter),
            new ViewPage());
        //This fails because it can't extract route information like the controller name)
        html.RenderPartial(viewName, model);
        return sb.ToString();
    }


    public void setupTestEnvironment(Controller controller)
    {
        RouteTable.Routes.Clear();
        RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        RouteTable.Routes.MapRoute(
            "Default", 
            "{controller}/{action}/{id}", 
            new { controller = "Main", action = "DefaultView", id = UrlParameter.Optional } 
        );




        ModelBinders.Binders[typeof(DateTime)] = new DateTimeModelBinder();
        ModelBinders.Binders[typeof(DateTime?)] = new DateTimeModelBinder();
        ModelMetadataProviders.Current = new DateTimeMetadataProvider();



    }
}

И вот мой метод испытаний:

        [TestMethod]
    public void GetUserTableView()
    {
        ViewResult result = controller.UserTable() as ViewResult;

        //I can populate the route and handler on the controller...
        RouteData routes = RouteTable.Routes.GetRouteData(controller.HttpContext);
        controller.RouteData.Route = routes.Route;
        controller.RouteData.RouteHandler = routes.RouteHandler;
        RouteValueDictionary routeKeys = new RouteValueDictionary();
        routeKeys.Add("controller", "UserManagement");
        routeKeys.Add("action", "UserTable");
        //But the RouteData.Values collection is read only :(
        controller.RouteData = new RouteData(){Values = routeKeys};
        string renderedView = WebTestUtilities.RenderRazorViewToString(result.ViewName, result.Model, controller);
    }

Кстати, конкретная ошибка, которую я получаю: RouteData должен содержать элемент с именем 'controller' с непустым строковым значением.

2 ответа

Решение

Возможно, вы уже работали над этим, но у меня была похожая проблема, и я решил установить RouteData, сбросив ControllerContext на контроллере следующим образом:

RouteData routeData = new RouteData();
routeData.Values.Add("someRouteDataProperty", "someValue");
ControllerContext controllerContext = new ControllerContext { RouteData = routeData };
controller.ControllerContext = controllerContext;

и затем в контроллере RouteData.Values.ContainsKey("someRouteDataProperty") работает, как вы настроили в тесте.

Это работает, потому что есть конструктор ControllerContext без параметров, который специально предназначен для насмешек и т. Д.

Вы можете переопределить Controllers ControllerContext.RouteData - его виртуальное свойство и свойство RouteData просто используют его для чтения.

Из отраженного кода свойства asp.net mvc RouteData:

public RouteData RouteData 
{ 
    get 
    {
        return ControllerContext == null ? null : ControllerContext.RouteData; 
    }
}
Другие вопросы по тегам