Pages

Sunday, 23 June 2013

Angular JS is known for and is becoming increasingly popular due to its nature of testability. Angular’s in-the- box abstractions make any amount of code easily testable. Code written using Angular JS can be tested using any JavaScript unit testing framework out there, may it be QUnit, Jasmine, Mocha or some other library.

If you haven’t followed earlier posts, take a look at the code on GitHub.

QUnit, Sinon and setting up

QUnit is a JavaScript unit testing framework developed by jQuery team. It is used in the projects like jQuery, jQuery UI, jQUery Mobile. QUnit is a very simple and generic framework that can be used to test any piece of JavaScript code. Unlike Jasmine, QUnit doesn’t have built-in support for creating spies.

Sinon is a JavaScript library that makes the process of creating spies, mocks and stubs easier. It doesn’t depend on any other JavaScript library and easily integrates with any JavaScript unit test framework. Official site has a nice API documentation covering different features of the library.

QUnit tests run on an HTML page. We need to add references to following files in the page:

QUnit library

QUnit style sheet

Sinon library

Source files of the scripts to be tested

File containing unit tests for above source

Any other library (like jQuery, jQuery UI, Angular) on which the source depends

As we will be testing code written using Angular JS, we should include angular-mocks.js library, which has mock services to replace some of the most commonly used services.

QUnit has a set of blocks to create modules, setup required resources, clear them after running the tests, create a test suit and a number of assertions. Following are the blocks that we will be using:

module: Defines a module, which can be used to group a number of related tests. Two functions namely, setup and teardown can be added to the module to instantiate and clear the resources for unit tests

test: Used to define a test spec. Accepts a name and function containing logic to be executed. The logic contains assertions to evaluate the behaviour

ok: A boolean assertion, that passes when the argument returns a Boolean true

equal: An assertion that compares two values and passes if they are equal. We also have notEqual assertion that passes if the values are not equal

Sinon is a very rich library with a huge API and a lot of features. We need very few of them in our specs:

spy: A spy can be an anonymous function or a wrap around an existing function. There are a number of ways to create and check if the spy is invoked. We will be using one of the approaches.
sinon.spy(obj,”methodName”) – creates a spy for a method with specified name
obj.methodName.called – to check if the method is called
obj.methodName.restore() – to restore the original functionality of the method

stub: Stubs are spied with existing behaviour. They should be used when we want a function to behave in a way we want. We will be using the following syntax to create a stub in the unit test that we are going to write in a while:
sinon.stub(obj,”methodName”, functionName) – Replaces the function with passed name with the logic of an existing function

Unit testing ShoppingCartController

Let’s start testing the functions defined in ShoppingCartController.

Dependencies of the controller are clearly visible from the signature. As we need to inspect behaviour of the controller in isolation, we must mock these services. Following is the signature of ShoppingCartController:

function ShoppingCartCtrl($scope, $window, shoppingData, shared) {
}

As these services will be used across specs, it is better to create them globally and initialize them in setup block. Since we will not hit the actual service, we need to use some static data to make the job of testing AJAX calls easier.

Unlike Jasmine, in QUnit tests we need to use injector to get the dependencies resolved. They aren’t resolved automatically. Following statement gets an instance of the injector:

injector = angular.injector(['ng', 'shopping', 'appMocks']);

First and most important dependency of the controller is the $scope service. We need to create our own scope and pass it as a parameter while creating object of the controller. Using $rootScope, it is very easy to create our own scope.

ctrlScope = injector.get('$rootScope').$new();

Second dependency is the $window service. As we are using href property of location alone, we can create a custom object with this property alone.

windowMock = { location: { href: ""} };

Third dependency shoppingData service is a wrapper to call backend data services. It used another service, $http to send AJAX requests. Angular JS team has made our job easy by creating $httpBackend, a mock for $http. $httpBackend provides a nice interface to send our own response when an AJAX request is made. In QUnit tests, a decorator statement must be added to get the object of $httpBackend. Following snippet shows it:

shoppingData service has three functions: getAllItems, addAnItem and removeItem. We need to create stubs for these functions with a call to the original function. The stubs will be used to inspect if the function is called. Following snippet demonstrates it:

Note: If you have already read the post on Jasmine, you may skip rest of the post and check the code as most of the explanation remains same

On creation of the controller, it calls getAllItems function of shoppingData service to fetch details of all items. The test for this behaviour should check if the right function is called and if it sets value to the items property. Following test shows this:

Upon calling addItem function of the controller, it calls addAnItem function of shoppingData service. As it makes an HTTP post request to the service, $httpBackend should be configured to respond when it finds a post request. Test looks as follows:

The function removeItem can also be tested in similar way. But, what if a request fails? The $errorMessage property of scope should be assigned with a friendly error message. A request can be forced to fail by passing a JavaScript object literal with an error status code to $httpBackend. Let’s see this in action:

mySortFunction is used to convert numeric value to number. We can test this function by passing a number in the form of a string and checking if it returned a number to us. We need to set the property sortExpression before calling the function.

On click of Purchase Items link on the page, the user has to be navigated to CheckoutItems view and setCartItems function of shared service should be called to pass the items array to the second view. As we are setting navigation URL to window.location.href, for which we created a mock, the test has to check if this property is set to some value. Following test verifies these functionalities:

Now view the test runner page on browser. All the tests should be passed. I encourage you to play a bit with the code and tests and check the result of test after the changes. This way, we will get to know more about using QUnit, Sinon with Angular JS and also about unit testing.

You can download the code including unit tests from the following GitHub repo: AngularShoppingCart

Saturday, 8 June 2013

Form validation is an integral part of any typical business application. In one of my posts, I demonstrated validating user inputs using Angular JS. The approach used in that post shows the error messages as soon as the user enters some data in the input fields. But, in some scenarios the users like to see the error messages just before the values are submitted. In this post, we will see an example with such behavior.

Following is the HTML mark-up of the form that we will be validating in this post:

While debugging the script of this form using a browser’s developer tools, I found the property $scope.itemForm.$error set with details of failed validations in a form. Following screenshot shows the contents of this property at an instance:

One can easily observe the following in the above screenshot:

Three of the validations of the form were failed here: a pattern based validator, a required field validator and a custom validator that checks for validity of price

For each type of validation, an array gets created internally that holds the details of the fields on which the validations were failed

To show the messages in bulk, we need to inspect these arrays and append the error messages dynamically. To keep it simple, let’s store the error messages all of the fields in an object.

As stated earlier, we have to loop through the properties of $error property to get the errors in the form. Using this data, we will pick the appropriate error message from the object created above and push the message into an array. Following code does this for us:

if($scope.itemForm.$dirty && $scope.itemForm.$invalid){
for(validations in $scope.itemForm.$error){
for(count=0;count
You can play with the sample on jsfiddle:

About Me

I am a software developer working on and passionate about Microsoft Technologies and anything new in the Web world. Lately, spending a lot of time with JavaScript and finding it much deeper than what I knew for past few years. I enjoy writing code and writing about code. My contribution to community earned me a Microsoft MVP award (ASP.NET/IIS) and a DZone MVB award. When not in front of computers, like to spend time with melodious Hindi and Telugu tunes or watching Cricket