8

Ich habe mich durch Scott Guthries ausgezeichneten Post auf ASP.NET MVC Beta 1 gearbeitet. Darin zeigt er die Verbesserungen an der UpdateModel-Methode und wie sie Unit-Tests verbessern. Ich habe ein ähnliches Projekt neu erstellt, aber jedes Mal, wenn ich einen UnitTest mit einem Aufruf von UpdateModel ausführe, erhalte ich eine ArgumentNullException, die den Parameter controllerContext benennt.Wie teste ich Unit-Test-Aktionen ohne Mocking mit UpdateModel?

Hier die entsprechenden Bits, mit meinem Modell starten:

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

Der Controller Aktion:

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

Und schließlich Test meiner Einheit das ist versagt:

[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 geworfen wird durch den Aufruf an UpdateModel mit der Nachricht "Wert kann nicht null sein. P arameter name: controllerContext ". Ich nehme an, dass die UpdateModel die System.Web.Mvc.ControllerContext erfordert, die während der Ausführung des Tests nicht vorhanden ist.

Ich gehe auch davon aus, dass ich irgendwo etwas falsch mache und nur in die richtige Richtung zeigen muss.

Hilfe bitte!

Antwort

5

Ich glaube nicht, dass es getan werden kann, da TryUpdateModel, das UpdateModel verwendet, den ControllerContext referenziert, der null ist, wenn er von einem Komponententest aufgerufen wird. Ich benutze RhinoMocks, um die verschiedenen Komponenten, die der Controller benötigt, nachzuahmen.

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

Hier ist das entsprechende Bit von der Controller.cs Quelle auf 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

Ich stimme zu, ich könnte dies durch Mocking lösen, aber das geht explizit gegen das, was Scott in seinem Beitrag in Bezug auf die UpdateModel Beispiele sagt: "Wir mussten nichts zu Unit-Test beide der oben genannten Formular Vorlage Szenarien verspotten. " –

+0

Ich schaute auf die Quelle für TryUpdateModel (die UpdateModel verwendet) und es verwendet definitiv den ControllerContext. Ich habe meine Antwort mit dem entsprechenden Quellcode-Bit aktualisiert. – tvanfosson

+0

@Hellfire, Ich sah einen Kommentar auf dem Blog-Post, der darauf hinweist, dass mindestens eine andere Person den gleichen Fehler hatte. Möglicherweise hat sich ModelBindingContext geändert, bevor Beta 1 aus der Tür kam. – tvanfosson

0

Oder Sie können Formulardaten-Proxy, wie

public class CountryEdit { 
    public String Name { get; set; } 
    public String Iso3166 { get; set; } 
} 
    erstellen
  • Plus. Einfache Einheitentests erstellen
  • Plus. Definieren Sie die weiße Liste der Felder, die vom Post
  • Plus aktualisiert werden. Einfache Setup-Validierungsregeln, einfach testen.
  • Minus. Sie sollten Datum von Proxy bewegen Sie

So modellieren Controller.Action aussehen sollte, wie

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

Ich war das gleiche Problem haben. Nachdem ich die Lösung von tvanfosson gelesen hatte, probierte ich eine einfache Lösung, die kein Mock-Framework enthielt.

eine Standard-Controller an den Controller hinzufügen wie folgt:

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

der Fehler dadurch gut für mich, während Unit-Tests entfernt. Ich hoffe, dass dies jemand anderem helfen kann.