2012-04-11 7 views
0

Es ist eine Weile her, seit ich MVC gearbeitet habe, so dass ich hoffentlich etwas vermisse. Ich versuche, eine Test- und Controller-Aktion zu schreiben, um einfach ein DTO namens "Business" zu bearbeiten.Grundlegende MVC3 UnitTest schlägt fehl UpdateModel()

Controller Aktion:

[HttpPost] 
public ActionResult Edit(string id, Business business) 
{ 
    try 
    { 
     var model = _businessRepository.Get(id); 

     if (model != null) 
     { 
      UpdateModel(model); 

      if (ModelState.IsValid) 
      { 
       _businessRepository.Save(model); 
      } 
      else 
      { 
       return View(business); 
      } 
     } 

     return RedirectToAction("Index"); 
    } 
    catch 
    { 
     return View(); 
    } 
} 

Test:

[TestMethod] 
public void Edit_Post_Action_Updates_Model_And_Redirects() 
{ 
    // Arrange 
    var mockBusinessRepository = new Mock<IBusinessRepository>(); 
    var model = new Business { Id = "1", Name = "Test" }; 
    var expected = new Business { Id = "1", Name = "Not Test" }; 

    // Set up result for business repository 
    mockBusinessRepository.Setup(m => m.Get(model.Id)).Returns(model); 
    mockBusinessRepository.Setup(m => m.Save(expected)).Returns(expected); 
    var businessController = new BusinessController(mockBusinessRepository.Object); 

    // Act 
    var result = businessController.Edit(model.Id, expected) as RedirectToRouteResult; 

    // Assert 
    Assert.IsNotNull(result); 
    Assert.AreEqual(result.RouteValues["action"], "Index"); 
    mockBusinessRepository.VerifyAll(); 
} 

Die Linie, die es auf eine Ausnahme geben, ist das Updatemodel() in der Steuerung. Die Ausnahmedetails sind:

"Wert nicht null sein kann Parametername:. Controller"

+1

gibt es etwas, in dem Modell, wenn Sie den Anruf sparen Methode? Was ist der Code für das Modell? – Brian

+0

Ohne den Code für 'UpdateModel()' ist es schwer zu sagen, aber mein * rate * ist, dass es sich auf einen db-Kontext verlässt, der nicht von Ihrem Komponententest erstellt wird. – GalacticCowboy

+0

@Brian Ich komme nie auf die Save-Methode, weil es auf UpdateModel stirbt. Aber das Modell ist einfach 2 Strings: Id und Name. – mandreko

Antwort

0

Ich habe es geschafft, zu bekommen, was ich arbeiten wollte, indem Sie AutoMapper anstelle der Update.

Ich habe in meiner AutoMapper Initialisierung (IPersistable ist eine Schnittstelle für alle meine DTOs):

Mapper.CreateMap<IPersistable, IPersistable>().ForMember(dto => dto.Id, opt => opt.Ignore()); 

ich dann zu meinem Controller-Aktion geändert:

[HttpPost] 
public ActionResult Edit(string id, Business business) 
{ 
    try 
    { 
     var model = _businessRepository.Get(id); 

     if (model != null) 
     { 
      Mapper.Map(business, model); 

      if (ModelState.IsValid) 
      { 
       _businessRepository.Save(model); 
      } 
      else 
      { 
       return View(business); 
      } 
     } 

     return RedirectToAction("Index"); 
    } 
    catch 
    { 
     return View(); 
    } 
} 

Und änderte mein Test:

1

Ich habe einen Code auf Gist, den ich normalerweise verwenden, um diesen ControllerContext einzurichten. Der Code ist eine modifizierte Version, die ursprünglich aus Hanselmans Blog stammt.

https://gist.github.com/1578697 (MvcMockHelpers.cs)

+0

Auch dies verursacht eine "Objektreferenz nicht auf eine Instanz eines Objekts gesetzt." Fehler bei UpdateModel. Es scheint immer noch nicht zu mögen. – mandreko

1

Setup-Controller-Context

Hier finden Sie Codeausschnitt aus einem Projekt, das ich arbeite, so ist es vielleicht zu viel für Sie

public class TestBase 
    { 
     internal Mock<HttpContextBase> Context; 
     internal Mock<HttpRequestBase> Request; 
     internal Mock<HttpResponseBase> Response; 
     internal Mock<HttpSessionStateBase> Session; 
     internal Mock<HttpServerUtilityBase> Server; 
     internal GenericPrincipal User; 

      public void SetContext(Controller controller) 
      { 
       Context = new Mock<HttpContextBase>(); 
       Request = new Mock<HttpRequestBase>(); 
       Response = new Mock<HttpResponseBase>(); 
       Session = new Mock<HttpSessionStateBase>(); 
       Server = new Mock<HttpServerUtilityBase>(); 
     User = new GenericPrincipal(new GenericIdentity("test"), new string[0]); 

       Context.Setup(ctx => ctx.Request).Returns(Request.Object); 
       Context.Setup(ctx => ctx.Response).Returns(Response.Object); 
       Context.Setup(ctx => ctx.Session).Returns(Session.Object); 
       Context.Setup(ctx => ctx.Server).Returns(Server.Object); 
       Context.Setup(ctx => ctx.User).Returns(User); 

       Request.Setup(r => r.Cookies).Returns(new HttpCookieCollection()); 
       Request.Setup(r => r.Form).Returns(new NameValueCollection()); 
     Request.Setup(q => q.QueryString).Returns(new NameValueCollection()); 
       Response.Setup(r => r.Cookies).Returns(new HttpCookieCollection()); 

       var rctx = new RequestContext(Context.Object, new RouteData()); 
controller.ControllerContext = new ControllerContext(rctx, controller); 
      } 
} 

Dann in Ihren Tests können Sie vereinbaren:

//Arrange 
SetContext(_controller); 
Context.Setup(ctx => ctx.Request).Returns(Request.Object); 

Wenn Sie testen möchten Ihre Methode mit Model Fehler, fügen:

_controller.ModelState.AddModelError("Name", "ErrorMessage"); 
+0

Während dies aussieht, was ich gelesen habe, verursacht es immer noch den Code auf dem UpdateModel aus irgendeinem Grund zu sterben. "Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt." – mandreko

+0

Ich sehe, dass ich die Antwort eingerichtet habe, die in Ihrem Fall die Anfrage sein muss. Ich denke das ist auch deine Null Referenz Ausnahme –

0

Ich hatte das gleiche Problem und verwendet die Stack-Trace, um dies an den ValueProvider feststecken. Aufbauend auf Andrew Antwort oben für einige der zugrunde liegenden Objekte, die von einem Controller spöttisch, ich es geschafft, die Nullwert Ausnahme zu lösen, indem auch die Valueprovider wie dieser spöttischen:

var controller = new MyController(); 

// ... Other code to mock objects used by controller ... 

var mockValueProvider = new Mock<IValueProvider>(); 
controller.ValueProvider = mockValueProvider.Object; 

// ... rest of unit test code which relies on UpdateModel(...)