Thursday, December 27, 2012

JavaScript - MVVM and TDD.

Back in 2000, when I started my programming career, JavaScript was quite a negligible language which was used only to support quite a poor client side UI.
As a result, most programmers didn't pay much attention to the quality of their JS code as they did with their server side ASP/JSP code.

Over the years, the nature of the web applications changed constantly and the direction was obvious: richer client side applications and that meant much bigger JavaScript code and a lot more logic on the client side.

Unfortunately, most programmers haven't absorb this change and they still treat JavaScript as they did for the last decade - no OOP principles, no continues integration, no code conventions, no TDD, no MV{X} patteren and no design - in that manner, JavaScript is still a stepson.

Our small donation to change this situation is this wonderful serious of posts about JavaScript best practices.
In this post I would like to add 2 more very important aspects that will greatly push your JavaScript forward: MVVM and TDD.

MVVM
Rich client applications encapsulate a huge amount of logic - UI logic. This logic must be tested, especially in a dynamic language such as JavaScript which has no compilation process. Luckily, many tools for JavaScript unit testing have emerged lately and they are all capable of testing web UI. But, if your client side code is strongly coupled with UI elements - you're going to face many difficulties to test your code with those tools. The easiest way to work with them is to supply them a pure JavaScript that involves only logic and no UI elements.

Suppose we have the following requirement: in a travel agency, to search a vacation, a user can fill up 2 date fields - From Date and To Date.
When the user presses the search button, the application checks if From Date is bigger than To Date and if so, it will show a validation message, else, it will do an ajax query.

The code will probably look something like this:
search = function(){   
   var fromDate = $('#txtFromDate');
   var toDate = $('#txtToDate')
   if(fromDate > toDate){
       $('#spnDatesValidation').css('display', 'inline');   
   } else {
       $.ajax(...)
   }
}

This code can only run within a container that contains all the DOM elements - most probably a web browser. Also, you will have to supply the relevant HTML to that container.
As stated, you're going to face many difficulties to automate a test for this code.

Here comes MVVM to the rescue. MVVM stands for Model-View-ViewModel and it's an MV{X} pattern. This family of patterns also includes MVC and MVP and they all give you the ability to decouple the UI logic from the View. In the MVVM pattern, the UI logic resides in the ViewModel.
To achieve this decoupling,  MVVM relies heavily on a binding mechanism. A binding mechanism usually binds between a UI control i.e. TextBox, ComboBox etc. and a property of the ViewModel.
In our example it will bind between the txtFromDate (which is a TextBox) and ReservationViewModel.fromDate. And so, whenever the user changes txtFromDate, the binding mechanism automatically updates ReservationViewModel.fromDate and it does this behind the scenes. Of course, the same goes the other way around.
This means that whenever the user presses the search button, ReservationViewModel.fromDate and ReservationViewModel.toDate are already updated.

And so, our new code can now look something like this:
ReservationViewModel = function(){
   this.search = function(){   
       //at this stage, fromDate and toDate are already updated
       if(this.fromDate > this.toDate){
           this.showDatesValidation = true;   
       } else {
           $.ajax(...)
       }  
   }
}

In the code above we also assume that the visibility of spnDatesValidation (which is probably a SPAN that shows a validation error) is bound to ReservationViewModel.showDatesValidation.
As you can see, this code is no longer coupled with any DOM element - it's a plain JavaScript code that can run on any JavaScript engine with no need to supply a HTML for that engine.

A great tool that provides a binding mechanism for JavaScript is Knockout.js. It's probably the most popular MVVM tool these days. We are working with that tool for several months now and it looks robust, simple, mature and it has a great documentation.

TDD
In this post i assume that the readers are already familiar with TDD and its concepts like stubs, mocks and dependency injection.

Stubs & Mocks
Looking again at the last code sample above, we can see that the search method is not quite testable yet - it's performing an ajax call and probably has some logic regarding a successful or a failure response.
Suppose we have a test called "search with valid dates should make an ajax call". This test supplies valid dates to the ViewModel and expects to see that an ajax call has been made. The problem is that with the current code, there is not a clean way to assert that an ajax call has been made.

Dependency Injection might help us out here. What we can do is to inject an object that encapsulates all ajax calls regarding the ReservationViewModel. Thus, we will be able to inject a stub object that will record any call its own methods.

So let's change the method a little bit, lets use Dependency Injection:
ReservationViewModel = function(reservationAjax){   
   
   this.reservationAjax = reservationAjax;
   
   this.search = function(){   
       if(this.fromDate > this.toDate){
           this.showDatesValidation = true;   
       } else {
           this.reservationAjax.search(...);
       }  
   }
}

And the test will look something like this (here i use Buster.js for testing):
buster.testCase("ReservationViewModel tests", {
    "search with valid dates should make an ajax call": function () {
        var ajaxStub = new Object();
        var ajaxCalled = false;
        ajaxStub.search = function(){
            ajaxCalled = true;
        }
        var vm = new ReservationViewModel(ajaxStub);
        vm.fromDate = new Date(2000, 1, 1);
        vm.toDate = new Date(2001, 1, 1);
    
        vm.search();
      
        assert.equals(ajaxCalled, true);
    }
});

As you can see, I dynamically override the search method of the ajaxStub object and adjust it to my needs - raise a flag whenever this method is being called.

Conclusions 

  • TDD is a very good practice and it's almost a must for JavaScript for 2 resons: 
    • JS is a dynamic language and it has no compilation process. TDD should compensate for this.
    • UI logic is a lot of logic and quite a complicated one. This logic must be tested. 
  • You can test your JS code even without any MV{X} pattern but this will probably cause you a lot of headaches. Use MVVM - it will save you a lot of code and it will improve your design and testability.
  • Use Dependency Injection for things like ajax and message boxes - it will give you a greater flexibility.