Creating a Testable Client-side ViewModel with Knockout

In my previous post, I discussed why I a JavaScript MVC or MVVM framework is essential: It lets you prove, quickly and conveniently, that your code works so that you can assemble applications out of reliable components. In that post I also started on a project to show how one of those frameworks—Knockout—supports the MVVM design pattern. I also created the server-side methods that my application (an ASP.NET View that displays information for a customer selected by the user) would need. However, what I created were mock functions that I could use for testing. In this post, I’ll start to create the client-side resources my application would need while still focussing on testing the code.

Setting Up the Client

My first step on the client (after adding the Knockout library to my Scripts folder, of course) is to add the necessary script references to my layout page to support using Knockout. For testing purposes, I also include a RenderSection that will allow me to selectively add script references on a View-by-View basis. This basic set of references is used by every View:

<head> <meta charset="utf-8" />  
 <title>@ViewBag.Title</title>
 <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> 
 <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> 
 <script src="@Url.Content("~/Scripts/knockout-2.0.0.js")" type="text/javascript"></script>
 @RenderSection("Header",false)
</head> 

With those references in place I’m ready to start generating some client-side code. First, I add a Javascript file to my Scripts folder to hold the Customer ViewModel that I’ll developer. Again, to support testing, I add a variable called controller to the file that will let me redirect my server-side requests to my mock methods:

var controller = ""; 

Now I can (finally) start defining my ViewModel, beginning with the properties that hold the data my page will both display and allow users to update. I define those properties using Knockout’s observable function:

var Customer = function (custIdIn) {
 this.CustId = ko.observable(); 
 this.CompanyName = ko.observable(); 
 this.City = ko.observable(); 

Using the observable function gives me two-way databinding (the ViewModel automatically updates the View’s elements, the View’s elements automatically update the ViewModel). Other Knockout functions support handling arrays and computed values. I don’t have to use the observable function—if I only wanted the data to flow from my Customer ViewModel to the elements on my page, I could skip using the observable function.

I also define a variable to refer to the current value of this (my ViewModel). I’ll need a reference to my ViewModel later and defining self at this point in the ViewModel’s code bypasses any of the ways that this gets redefined:

var self = this; 

My next step is, still in my Customer ViewModel, to define a function that uses jQuery’s getJSON method to retrieve data from my server-side methods. I’m going to call this function getCustomer and have it retrieve the Customer object from my server-side methods. Once the JSON object is retrieved, the code updates my ViewModel’s properties from the JSON object’s properties. This is one of the places that I use the self variable I defined earlier to avoid problems with this being redefined. I also use my controller variable to define the URL used to access the service:

this.getCustomer = function (custId) {
  $.getJSON("/" + controller + "/CustomerById/" + custId, 
 null, 
 function (cust) {
 self.CustId(cust.CustomerID);
 self.CompanyName(cust.CompanyName);
 self.City(cust.City); } ); }; 

Because my CustId, CompanyName, and City properties are really Knockout observable functions, I must use the syntax shown here to update their values. This ensures, for instance, that changes to the CompanyName and City properties will automatically be reflected in any elements in my page that the properties are databound to.

Testing a JavaScript ViewModel

I’ve got enough functionality written at this point to begin testing (too much, actually, for a real TDD approach—I should have started writing my tests before I put any code in my getCustomer function). In my last post, I defined a controller (TestController) and an action method (CustomerViewModelTest) to use for testing. I can now create the View that will be called from that action method (CustomerViewModelTest.cshtml). I use QUnit for testing client-side code, so I need to add the files Qunit needs (a JavaScript file and a CSS file) to my application. However, I’d prefer not to download those files on every page so I don’t add those references my layout View. Instead, in my test View, I add the references to the Header section that my Layout View references. I also add a reference to the JavaScript file holding my Customer ViewModel that I want to test:

@{ ViewBag.Title = "Customer ViewModel Tests"; } 
@section Header { 
 <script src="@Url.Content("~/Scripts/CustomerVieModel.js")" type="text/javascript"></script>  
 <link href="@Url.Content("~/Content/Tests/qunit.css")" rel="stylesheet" type="text/css" />  
 <script src="@Url.Content("~/Scripts/qunit.js")" type="text/javascript"></script>  
} 

In the body of my page, I add in the headers that QUnit needs to execute and display test results:

<form action="/Test/index">
 <h1 id="qunit-header">Customer MVVM Tests</h1>
 <h1 id="qunit-bannder"></h1>
 <div id="qunit-testrunner-toolbar"></div>
 <h2 id="qunit-userAgent"></h2>
 <ol id="qunit-tests"></ol>
</form> 

I can now test my ViewModel using QUnit’s test function. I set my controller variable to direct all my getJson requests to the mock methods on my Test controller. I use jQuery to prevent my tests from running until the page is fully loaded:

controller = "Test"; $(function () { 

That test consists of instantiating my Customer ViewModel, calling its getCustomer method passing a customer Id, and checking to see if the ViewModel’s properties were updated correctly. Since I’m making an asynchronous server-side call, I can’t simply call my getCustomer method and check the property results—my test code might run faster than my server-side call and check the property before the result is returned. To get around that, I use QUnit’s asyncTest and JavaScript’s setTimeout functions to wait for one second before checking the result using QUnit’s equals function:

var cust; test("Get Customer",
 function () {
 stop();
 cust = new Customer("Test");
 cust.getCustomer("ALFKI"); 
 setTimeout( function () {
 equals(cust.City(), "Regina", "Customer not retrieved");
 start(); },
 3000); } ); 

I can now run my tests and see the results by calling my CustomerViewModel action method on my TestController (and, of course, my test passes the first time).

At this point I have a testable ViewModel but, of course, that’s not much good unless it supports a real page. That’s what I’m going to look at in my next post. But with this framework in place, I know that I’m building my page with something that works.

If you’re looking for a course with a good introduction to TDD in Visual Studio, Learning Tree’s Design Patterns and Best Practices in .NET course is a good choice. However, Learning Tree’s ASP.NET MVC course devotes a whole chapter to TDD with ASP.NET MVC.

Peter Vogel

Type to search blog.learningtree.com

Do you mean "" ?

Sorry, no results were found for your query.

Please check your spelling and try your search again.

maltcasino maltcasino maltcasino bedava bahis güvenilir bahis siteleri canlı bahis siteleri
bedava bahis Yatırımsız Deneme Bonusu bonus veren siteler