Добавьте ключи и значения в 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;
}
}