UpdateModel, FormCollection and unit test

· March 29, 2009

I ran into some problems with a Action Controller method that accepted an FormCollection. Inside the method I use UpdateModel to get the values from the form into the model-class.

Here is a skeleton implementation of the method:

[UPDATED] **Some insightful comments by Steve made me do some changes to this code. I have now updated it. The tip had to do with if the ValueProvider should be set on the Controller in the test or in the actual Action-method. I finally opted for the latter and changed it into this (significant code in **bold).

\[AcceptVerbs(HttpVerbs.Post)\] public ActionResult Create(FormCollection form) { // Create ViewData ProductForm viewData = new ProductForm(new Product(), productOwnerRepository.Find().ToList());   try { **UpdateModel(viewData, form.ToValueProvider());**   // Get the productowner for the selected product-owner id viewData.Product.Owner = productOwnerRepository.GetById(viewData.SelectedOwnerID);   // add new product to the repository productRepository.Add(viewData.Product); productRepository.Save();   // Go back to the list return RedirectToAction("Index"); } catch { ModelState.AddRuleViolations(viewData.Product.GetRuleViolations()); return View(viewData); } } </div>

This works great but when I call this from my unit test the the values of my form (created in the unit test) is lost. I was very puzzled by this until I found this post in the asp.net mvc forum.

So what that means is that you’ll, in the unit test case, need set the ValueProvider, for the controller you’re testing, to the test form collection you’re sending to the method. Otherwise the Action Controller method will check the Request-property and that will be empty in the test case. Not obvious maybe but understandable when you think about it.

**[UPDATED] **Just to be sure – note that the Action-method is using an overloaded version of the UpdateModel that makes sure that the sent-in form is used. I haven’t used the recommendation from the link above since I think it’s less readable/understandable. The main point are still valid though.

Here is a short sample that test the action method above.

\[TestMethod\] public void createControllerActionCanTakeAFormCollectionWithProductDataAndAddItToTheRepository() { var numberOfProductsBefore = productRepository.Find().Count(); var form = CreateProductTestFormCollection();   var result = productController.Create(form);   Assert.IsNotNull(result); Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult)); var redirectResult = (RedirectToRouteResult)result; Assert.AreEqual("Index", redirectResult.RouteValues\["action"\]);   Assert.AreEqual(numberOfProductsBefore + 1, productRepository.Find().Count()); }   private static FormCollection CreateProductTestFormCollection() { FormCollection form = new FormCollection(); form.Add("Product.Name", TESTFORM_NAME); form.Add("Product.Description", TESTFORM_DESCRIPTION); form.Add("SelectedOwnerID", TestData.TEST_PRODUCTOWNER_ID2.ToString());   return form; } </div>

Twitter, Facebook