Как мне выполнить юнит тестовые действия без Mocking, которые используют UpdateModel?
Я работал над отличным постом Скотта Гатри в ASP.NET MVC Beta 1. В нем он показывает улучшения, внесенные в метод UpdateModel, и как они улучшают юнит-тестирование. Я воссоздал подобный проект, однако каждый раз, когда я запускаю UnitTest, который содержит вызов UpdateModel, я получаю ArgumentNullException, называющий параметр controllerContext.
Вот соответствующие биты, начиная с моей модели:
public class Country {
public Int32 ID { get; set; }
public String Name { get; set; }
public String Iso3166 { get; set; }
}
Действие контроллера:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Int32 id, FormCollection form)
{
using ( ModelBindingDataContext db = new ModelBindingDataContext() ) {
Country country = db.Countries.Where(c => c.CountryID == id).SingleOrDefault();
try {
UpdateModel(country, form);
db.SubmitChanges();
return RedirectToAction("Index");
}
catch {
return View(country);
}
}
}
И, наконец, мой модульный тест, который не проходит:
[TestMethod]
public void Edit()
{
CountryController controller = new CountryController();
FormCollection form = new FormCollection();
form.Add("Name", "Canada");
form.Add("Iso3166", "CA");
var result = controller.Edit(2 /*Canada*/, form) as RedirectToRouteResult;
Assert.IsNotNull(result, "Expected to be redirected on successful POST.");
Assert.AreEqual("Show", result.RouteName, "Expected to redirect to the View action.");
}
ArgumentNullException
брошен призывом к UpdateModel
с сообщением "Значение не может быть нулевым. Имя параметра: controllerContext". Я предполагаю, что где-то UpdateModel
требует System.Web.Mvc.ControllerContext
которого нет во время выполнения теста.
Я также предполагаю, что я где-то делаю что-то не так, и мне просто нужно указать в правильном направлении.
Помогите, пожалуйста!
3 ответа
Я не думаю, что это может быть сделано, поскольку TryUpdateModel, который использует UpdateModel, ссылается на ControllerContext, который является нулевым при вызове из модульного теста. Я использую RhinoMocks, чтобы смоделировать или заглушить различные компоненты, необходимые для контроллера.
var routeData = new RouteData();
var httpContext = MockRepository.GenerateStub<HttpContextBase>();
FormCollection formParameters = new FormCollection();
EventController controller = new EventController();
ControllerContext controllerContext =
MockRepository.GenerateStub<ControllerContext>( httpContext,
routeData,
controller );
controller.ControllerContext = controllerContext;
ViewResult result = controller.Create( formParameters ) as ViewResult;
Assert.AreEqual( "Event", result.Values["controller"] );
Assert.AreEqual( "Show", result.Values["action"] );
Assert.AreEqual( 0, result.Values["id"] );
Вот соответствующий бит из источника Controller.cs на www.codeplex.com/aspnet:
protected internal bool TryUpdateModel<TModel>( ... ) where TModel : class
{
....
ModelBindingContext bindingContext =
new ModelBindingContext( ControllerContext,
valueProvider,
typeof(TModel),
prefix,
() => model,
ModelState,
propertyFilter );
...
}
У меня была такая же проблема. Прочитав решение tvanfosson, я попробовал простое решение, не включающее фальшивый фреймворк.
Добавьте ControllerContext по умолчанию к контроллеру следующим образом:
CountryController controller = new CountryController();
controller.ControllerContext = new ControllerContext();
Это исправило ошибку для меня во время модульного тестирования. Я надеюсь, что это может помочь кому-то еще.
Или вы можете создать прокси данных формы, как
public class CountryEdit {
public String Name { get; set; }
public String Iso3166 { get; set; }
}
- Плюс. Легко создавать юнит-тесты
- Плюс. Определить белый список полей обновления из поста
- Плюс. Легко настроить правила проверки, легко проверить это.
- Минус. Вы должны перенести дату с прокси на вашу модель
Так должен выглядеть Controller.Action, вроде
public ActionResult Edit(Int32 id, CountryEdit input)
{
var Country = input.ToDb();
// Continue your code
}