Unit Testing ASP.Net MVC: Part 8, Testing Ajax and JSON

This is Part 8 in a series on unit testing MVC. Part 1 and an index to all posts in this series is here.

Just when you think you’ve done a good job with your application, good unit tests, users are happy, they bring you a change. In our case, the change calls for adding a country field to the Customer Create form. When the user picks the country, the State/Province field should populate with the states or provinces from that country. This will require making an AJAX call to the controller to get the list of states then returning that data via JSON.

We’ll need to create a Countries table and add a Country column to the States table. Then modify the State entity and add a Country Entity and supporting Repository, etc. Don’t forget to add the binding for Ninject and change the CustomerController constructor to accept the mocked Country table. You’ll need to change the tests to handle it too. Oh yes, a Country dropdown in Create.cshtml is needed too. I’ll be right here while you do all that.

Now that you made these changes we need to add a method to the CustomerController to return the JSON data.

   1:  public ActionResult GetStateValues(int countryId)
   2:  {
   3:      List<State> states = stateRepository.States
   4:  .Where(s => s.CountryId == countryId)
   5:  .OrderBy(s => s.Name).ToList();
   6:      var fs = from s in states
   7:                  select new
   8:                      {
   9:                          Code = s.Code,
  10:                          Name = s.Name
  11:                      };
  12:      JsonResult retVal = new JsonResult();
  13:      retVal.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
  14:      retVal.Data = fs.ToList();
  15:      return retVal;

 

We also need to add the javascript to the Customer Create.cshtml file to make the call

   1:  <script>
   2:      $(document).ready(function () {
   3:          $('#CountryId').change(function () {
   4:              $('#StateCode').html('');
   5:              $.getJSON('@Url.Action("GetStateValues")',
   6:  { countryId: $('#CountryId').val() }, function (data) {
   7:                  var items = "";
   8:                  $.each(data, function (i, item) {
   9:                      $("#StateCode").append('<option value="'
  10:  + item.Code + '">' +  item.Name + '</option>') ;
  11:                  });
  12:              });
  13:          });
  14:      });
  15:  </script>

Now compile and run the code. When you select a country, the javascript function is called. That function passes the selected country Id to the C# code in the controller. The states for that country are retrieved from the database and put into a JSON object, then return back to the page. The javascript code then loads the new items into the dropdown.

And finally, we’re at the test.. But it isn’t easy because the JSON result contains an anonymous type. We simply can’t call ToString(). In fact, if you do call result.Data.ToString(), the result will be System.Collections.Generic.List`1[<>f__AnonymousType4`2[System.String,System.String]]

Instead, we have to do what the .Net framework would do, serialize it. First, the UnitTests project needs a reference to the System.Web.Extensions assembly. Then add a using statement to System.Web.Script.Serialization.

   1:  [Test]
   2:  public void GetStateValues_Returns_ProperValues()
   3:  {
   4:      // Arrange
   5:      Mock<ICustomerRepository> mock = new Mock<ICustomerRepository>();
   6:      Mock<IStateRepository> stateMock = new Mock<IStateRepository>();
   7:      Mock<ICountryRepository> countryMock = new Mock<ICountryRepository>();
   8:  
   9:      stateMock.Setup(m => m.States).Returns(new State[]
  10:          {
  11:              new State {Id = 1, Code = "UT", Name = "Utah", CountryId = 2},
  12:              new State {Id = 1, Code = "CA", Name = "California", CountryId = 2},
  13:              new State {Id = 1, Code = "NY", Name = "New York", CountryId = 2},
  14:              new State {Id = 1, Code = "BC", Name = "British Colombia", CountryId = 1}
  15:          }.AsQueryable());
  16:  
  17:      CustomerController controller = new CustomerController(mock.Object,
  18:  stateMock.Object, countryMock.Object);
  19:  
  20:      // Act
  21:      JsonResult result = controller.GetStateValues(2) as JsonResult;
  22:      JavaScriptSerializer serializer = new JavaScriptSerializer();
  23:      string actual = serializer.Serialize(result.Data);
  24:  
  25:      // Assert
  26:      Assert.AreEqual(@"[{""Code"":""CA"",""Name"":""California""},
  27:  {""Code"":""NY"",""Name"":""New York""},
  28:  {""Code"":""UT"",""Name"":""Utah""}]", actual);
  29:  }

 

Line 21 gets the actual JsonResult from the controller. We pass a parameter of 2 to get the states in country 2. Line 22 instantiates the Serialize and then the result data is serialized in line 23. Lines 26-28 compare the expected data with what actually gets returned.

I have one more post to make in this series. In that I’ll summarize everything we’ve learned and address some nagging questions that you may have, some of what have been asked in the comments or direct emails to me.

MVC, Unit Testing
2 comments on “Unit Testing ASP.Net MVC: Part 8, Testing Ajax and JSON
  1. Pingback: Unit Testing ASP.Net MVC: Part 1, Getting Started « Developer.Blog();

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>