ASP.NET MVC Controller Test Ortamında UpdateModel Davranışları

4 Mar

Bu yazıda ASP.NET MVC ortamında test sınıflarımı hazırlarken karşılaştığım küçük bir ayrıntıyı analtmak istiyorum.  Bu noktadan itibaren kullanacağım örnek ASP.NET MVC3 ortamında yürütülen Nerddinner projesindeki Edit action metod testi olacaktır. Neden Edit action metodunu test ediyorum? Bunun sebebi Edit işlemi sırasında Controller temel sınıfının helper metodları olan UpdateModel veta TryUpdateModel metodlarının özel bir durumunun olması.

UpdateModel helper metodu, form ortamında bir bilgi güncellemesi yapıldığında, formdan  post yöntemi ile gelen değerleri ilgili nesneye bağlamak için kullanılır. Aşağıdaki örnekte görüldüğü üzere FormCollection içierisinden gelen form bilgileri, UpdateModel(dinner) ile dinner nesnesini güncellemektedir.


[HttpPost, Authorize]
public ActionResult Edit(int id, FormCollection collection) {

       Dinner dinner = dinnerRepository.Find(id);

       if (!dinner.IsHostedBy(User.Identity.Name))
            return View("InvalidOwner");

       try {
            UpdateModel(dinner);
            dinnerRepository.Save();
            return RedirectToAction("Details", new { id=dinner.DinnerID });
       }
       catch {
            return View(dinner);
       }
}

O halde bu noktadan sonra şunu anlıyoruz ki test ortamında da UpdateModel metodunu kullanan metodların testi için FormCollction nesnesi kullanmalıyız. Bu güncelleme işlemi nasıl yapılmaktadır? UpdateModel tarafından  Controller sınıfının “ValueProvider” özelliğine bir FormCollection nesnesi atanarak yapılmaktadır.

[TestMethod]
public void EditAction_Should_Redirect_When_Update_Successful() {

      // Arrange
      var controller = CreateDinnersControllerAs("SomeUser");
      int id = 1;

      FormCollection formValues = new FormCollection() {
         { "Dinner.Title", "Another value" },
         { "Dinner.Description", "Another description" }
      };

      controller.ValueProvider = formValues.ToValueProvider();

      // Act
      var result = controller.Edit(id, formValues) as RedirectToRouteResult;

      // Assert
      Assert.AreEqual("Details", result.RouteValues["Action"]);
      Assert.AreEqual(id, result.RouteValues["id"]);

}

Dikkat: FormCollection kullanılmayan test metodları UpdateModel metodunun çalıştırılması(invoke) sırasında NullReferenceException hatası verecektir.

Soru: Eğer Edit action metoduna parametre olarak FormCollection değilde nesnenin kendisi atılsaydı veya bir ViewModel patterne sahip bir nesne gönderilirse ne oladak?

DinnerFormViewModel adında bir nesne ve bu nesne içinde de Dinner tipinde Dinner adında bir property olduğunu varsayalım.


[HttpPost, Authorize]
public ActionResult Edit(int id, DinnerFormViewModel model) {

       Dinner dinner = dinnerRepository.Find(id);
       if (!dinner.IsHostedBy(User.Identity.Name))
            return View("InvalidOwner");

       try {
            UpdateModel(dinner, "Dinner");
            dinnerRepository.Save();
            return RedirectToAction("Details", new { id=dinner.DinnerID });
       }
       catch {
            return View(dinner);
       }
}

Bu durumda UpdateModel metodunun, prefix(ön ek) belirlenebilen versiyonunu kullanıyoruz. Yani DinnerFormViewMolel içerisindeki “Dinner” ile başlayan nesneler “ValueProvider” Property’sine bağlanacak.