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

Хорошо, поэтому я использую 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 ]

29 голосов
/ 02 апреля 2012

Возможно, вы уже с этим поработали, но у меня просто была похожая проблема, и я решил установить 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 без параметров, который специально существует для разрешения насмешек и т. Д.

2 голосов
/ 30 марта 2012

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

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

public RouteData RouteData 
{ 
    get 
    {
        return ControllerContext == null ? null : ControllerContext.RouteData; 
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...