8

He estado trabajando en la excelente publicación de Scott Guthrie en ASP.NET MVC Beta 1. En él, muestra las mejoras realizadas en el método UpdateModel y cómo mejoran las pruebas unitarias. He recreado un proyecto similar, pero cada vez que ejecuto un UnitTest que contiene una llamada a UpdateModel, recibo una ArgumentNullException que nombra el parámetro controllerContext.¿Cómo puedo probar las acciones sin simular que utilizan UpdateModel?

He aquí los bits correspondientes, empezando por mi modelo:

public class Country { 
    public Int32 ID { get; set; } 
    public String Name { get; set; } 
    public String Iso3166 { get; set; } 
} 

La acción del controlador:

[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); 
    } 
    } 
} 

Y por último, mi unidad de prueba que está fallando:

[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 se lanza por la llamada al UpdateModel con el mensaje "El valor no puede ser nulo. P nombre del parámetro: controllerContext ". Supongo que en algún lugar el UpdateModel requiere el System.Web.Mvc.ControllerContext que no está presente durante la ejecución de la prueba.

También estoy asumiendo que estoy haciendo algo mal en alguna parte y solo necesito apuntar en la dirección correcta.

Ayuda ¡Por favor!

Respuesta

5

No creo que se pueda hacer porque TryUpdateModel, que utiliza UpdateModel, hace referencia al ControllerContext que es nulo cuando se invoca desde una prueba unitaria. Uso RhinoMocks para simular o resguardar los diversos componentes que necesita el controlador.

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"]); 

Aquí está la parte relevante de la fuente Controller.cs en www.codeplex.com/aspnet:

protected internal bool TryUpdateModel<TModel>(...) where TModel : class 
{ 

    .... 

    ModelBindingContext bindingContext = 
      new ModelBindingContext(ControllerContext, 
            valueProvider, 
            typeof(TModel), 
            prefix, 
            () => model, 
            ModelState, 
            propertyFilter); 

    ... 
} 
+1

Estoy de acuerdo, podría resolver esto burlándose, pero eso va explícitamente en contra de lo que Scott dice en su publicación en referencia a los ejemplos de UpdateModel: "No tuvimos que burlarnos de nada para probar los dos escenarios de envío de formularios anteriores. " –

+0

Miré la fuente de TryUpdateModel (que utiliza UpdateModel) y definitivamente usa ControllerContext. Actualicé mi respuesta con el bit de código fuente relevante. – tvanfosson

+0

@Hellfire, vi un comentario en la publicación del blog que indicaba que al menos otra persona estaba teniendo el mismo error. Podría ser que ModelBindingContext cambió antes de que Beta 1 saliera por la puerta. – tvanfosson

0

O puede crear proxy de datos del formulario, como

public class CountryEdit { 
    public String Name { get; set; } 
    public String Iso3166 { get; set; } 
} 
  • Plus. Fácil crear pruebas unitarias
  • Plus. Defina la lista blanca de actualización de campos desde la publicación
  • Plus. Fácil configuración de reglas de validación, fácil prueba.
  • Menos. Debe moverse fecha de proxy para modelar

Así Controller.Action debe ser, como

public ActionResult Edit(Int32 id, CountryEdit input) 
{ 
    var Country = input.ToDb(); 
    // Continue your code 
} 
2

que estaba teniendo este mismo problema. Después de leer la solución de tvanfosson, probé una solución simple que no implica un marco simulado.

Añadir un ControllerContext predeterminado al controlador de la siguiente manera:

CountryController controller = new CountryController(); 
controller.ControllerContext = new ControllerContext(); 

Esto eliminó el error bien para mí, mientras que las pruebas unitarias. Espero que esto pueda ayudar a alguien más.

Cuestiones relacionadas