ASP.Net MVC – unit testing UpdateModel()

For an ASP.Net MVC solution, I wanted to write some unit tests for a controller action method that made use of Controller.UpdateModel(), and was surprised by the paucity of information on the topic.  Whilst there is nothing revolutionary here, it may prove useful to someone.  The key to the test is managing to supply the values of a posted FORM to the action method, whilst the signature of the method itself takes only a single identifier, such as an int, as a parameter.  As we will see, this is achieved using a FormCollection and the controller’s ValueProvider property.

Defining the View and Controller

To provide some context for this example, we have a form that allows us to update a product.  A product has a name, colour and id.  The form might look something like:

<%using(Html.BeginForm("UpdateProduct"))
  {%>
  <p>
     <label for="Name">Product Name:</label>
     <%=Html.TextBox("Name")%>
  </p>
  <p>
     <label for="Colour">Product Colour:</label>
     <%=Html.TextBox("Colour")%>
  </p>
  <%=Html.Hidden("Id")%>
  <input type="submit" value="Update" />
<%}%>

 

Our controller is pretty simple: it has an IProductProvider that will allow us to retrieve products, and a action method called UpdateProduct that is the action invoked by the above form.

public class ProductController : Controller
{
   private readonly IProductProvider _productProvider;
 
   public ProductController(IProductProvider productProvider)
   {
      _productProvider = productProvider;
   }
 
   [AcceptVerbs(HttpVerbs.Post)]
   public ViewResult UpdateProduct(int id)
   {
      var product = _productProvider.GetExistingProduct(id);
      UpdateModel(product);
      return View(product);
   }
}

 

The UpdateProduct method finds the product that matches the supplied id, and then updates the model with the Name and Colour values submitted in the form.

For completeness, the IProductProvider and Product are defined as follows:

public interface IProductProvider   
 {
    Product GetExistingProduct(int id);
 }
 
 public class Product
 {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Colour { get; set; }
 }

Testing UpdateModel()

In order to test UpdateModel, the test is going to need an HttpContext.  I used Rhino Mocks to create stubs for HttpContextBase and the controller’s IProductProvider :

[TestClass]
public class ProductControllerTests
{
   // Stubs
   private HttpContextBase _stubHttpContext;
   private IProductProvider _stubProductProvider;
 
   [TestInitialize]
   public void Init()
   {
      _stubHttpContext = MockRepository.GenerateStub<HttpContextBase>();
      _stubProductProvider = MockRepository.GenerateStub<IProductProvider>();
   }
}

 

Finally, the crux of this blog post: creation of the AAA test:

  1. Create two instances of Product, one to be returned by our call to IProductProvider.GetExistingProduct and another containing the new details supplied in the FORM’s POST.
  2. Prepare the IProductProvider.GetExistingProduct so that, when called, it returns the existing product.
  3. Initialise the ProductController, supplying the HttpContext stub.

Now we come to the key of the whole test: setting up the controller with the FORM values that would usually be posted by the View.  This can be done by creating a FormCollection that contains our test data and converting this into a type that we can assign to the ValueProvider property on the controller:

controller.ValueProvider = new FormCollection(
            new NameValueCollection()
               {
                  {"Name", newProduct.Name}, 
                  {"Colour", newProduct.Colour}, 
                  {"Id", newProduct.Id.ToString()}
               }
            ).ToValueProvider();

 

All that is left to do is complete the Act and Assert.  The full test looks like this:

[TestMethod]
public void ProductControllerTest_UpdateProduct_Expect_ProductModelUpdated()
{
   /***************
   * Arrange.
   ***************/
 
   //Define a couple of instances of Product.
   var originalProduct = new Product() { Id = 2, Colour = "Red", Name = "Roger" };
   var newProduct = new Product() { Id = originalProduct.Id, Colour = "Green", Name = "George" };
 
   //When GetExistingProduct is called, return the originalProduct instance of Product.
   _stubProductProvider.Stub(p => p.GetExistingProduct(Arg<int>.Is.Anything))
      .Return(originalProduct);
 
   //Create an instance of the ProductController, using our HttpContext stub.
   var controller = new ProductController(_stubProductProvider);
   controller.ControllerContext = new ControllerContext(_stubHttpContext, new RouteData(), controller);
 
   //Provide the controller with the test values in lieu of a genuine POST of a FORM.
   //We want to simulate the user submitting the details we defined in newProduct.
   controller.ValueProvider = new FormCollection(
      new NameValueCollection()
         {
            {"Name", newProduct.Name}, 
            {"Colour", newProduct.Colour}, 
            {"Id", newProduct.Id.ToString()}
         }
      ).ToValueProvider();
 
   /***************
   * Act.
   ***************/
   ViewResult viewResult = controller.UpdateProduct(newProduct.Id);
 
   /***************
   * Assert.
   ***************/
   var actualModel = viewResult.ViewData.Model;
   Assert.IsInstanceOfType(actualModel, typeof(Product));
   Assert.AreEqual(newProduct.Colour, ((Product)actualModel).Colour);
   Assert.AreEqual(newProduct.Id, ((Product)actualModel).Id);
   Assert.AreEqual(newProduct.Name, ((Product)actualModel).Name);
}

Comments

# <br /> ASP.NET MVC Archived Buzz, Page 1<br />
Gravatar <br /> ASP.NET MVC Archived Buzz, Page 1<br />
Left by Pingback/TrackBack on 1/21/2010 5:46 AM
# 
Gravatar Tech Tweets for 21-Jan-2010
Left by Web Dev .NET on 1/21/2010 8:12 AM
# Ephedrine buy online cheap.
Gravatar Ephedrine to buy. Buy ephedrine hcl.
Left by Ephedrine to buy. on 2/19/2010 12:37 PM
# Side effects of viagra.
Gravatar Viagra 6 free samples.
Left by Viagra. on 2/19/2010 4:27 PM
# Cheap ambein.
Gravatar Cheap ambein.
Left by Cheap ambein. on 2/20/2010 5:22 PM
# Phentermine capsule.
Gravatar Danger of phentermine.
Left by Purchase phentermine. on 2/21/2010 10:45 AM
# Vicodin without a prescription.
Gravatar Vicodin. Vicodin withdrawl. Symptoms vicodin addiction.
Left by Vicodin. on 2/21/2010 7:26 PM
# Does fioricet show up in blood work.
Gravatar Generic fioricet low prices amp fast delivery. Fioricet. Extract codeine from fioricet.
Left by Fioricet. on 2/22/2010 1:09 AM
# ����� ���������
Gravatar &lt;a href=&quot;www.ru-devochki-2010.ru/....html&quot;&gt;����� ���������&lt;/a&gt;
Left by ����� ��������� on 2/22/2010 4:44 AM
# ����������� ������
Gravatar &lt;a href=&quot;www.ru-devochki-2010.ru/....html&quot;&gt;����������� ������&lt;/a&gt;
# ���� �����
Gravatar &lt;a href=&quot;www.kartochniy-rai.ru/...be.html&quot;&gt;���� �����&lt;/a&gt;
Left by ���� ����� on 2/22/2010 8:02 AM
# Xanax.
Gravatar Xanax.
Left by Buy xanax. on 2/22/2010 2:50 PM
# Cheap valtrax.
Gravatar Cheap valtrax.
Left by Cheap valtrax. on 2/23/2010 6:09 AM
# Amoxicillin rash treatment.
Gravatar Amoxicillin. Amoxicillin rash infant. Amoxicillin drug interactions.
Left by Canine dosage amoxicillin. on 2/23/2010 6:40 PM
# Online phentermine.
Gravatar Phentermine online.
Left by Cheapest prices phentermine online. on 2/24/2010 6:20 AM
# Nhlhockey buy amp sell tickets online.
Gravatar Buy movie tickets online kingston ontario. Amtrak tickets online. Online movie tickets. Buy online your disneyland resort paris tickets.
Left by Cheap air tickets by cheap online flights. on 2/24/2010 5:17 PM
# ����� +��� ������ ���� ���
Gravatar &lt;a href=&quot;www.shkaff-deshevo.ru/...ye.html&quot;&gt;����� +��� ������ ���� ���&lt;/a&gt;

Leave Your Comment

Title*
Name*
Email (never displayed)
 (will show your gravatar)
Url
Comment*

Please add 2 and 7 and type the answer here:

Preview Your Comment.