Getting started Unit Testing in {{AngularJS}} Contd. – Part 20

This post is in continuation of my previous post on Unit Testing in AngularJS where we discussed about the basics of Unit Test in AngularJS, setting up the infrastructure and then wrote couple of unit tests. First we started with test for plain JavaScript method then we wrote an angular application and added unit test for Angular controller. While writing unit test, we also learned basics of Jasmine framework. The link of my previous post is given below

Getting started with Unit Testing in {{AngularJS}} – Part 19

Today we will write unit tests for following components.

  • Angular Service
  • Custom Filters
  • Custom Directive

Note – This post is a part of series of posts on AngularJS and it is 20th one. Click here to view all the post. Apart from that you can also use the navigation links for next and previous post in each post at the bottom to check all.

Testing your Service

As we know that service is an independent object which does some specific work and can be reused at multiple places. We normally used to have many services in our application which does various tasks. These services are the first candidates which should be considered for writing unit tests. Broadly in our service, we do two type of tasks, first where we take some input, write some logic and return the output accordingly. Second, where we connect some third party services via AJAX, process the response and return it. First type of service can be tested easily as normal JavaScript function. We are going to write Unit Test for second type.

We will extend our previous application where we hard coded the values in Angular Controller. Now instead, we will be creating an Angular service which will get the data from server and return that. Let’s see our service

homeModule.factory("TalksService", function ($http, $q) {
    return {
        getTalks: function () {
            // Get the deferred object
            var deferred = $q.defer();
            // Initiates the AJAX call
            $http({ method: 'GET', url: '/home/GetTalkDetails' }).success(deferred.resolve).error(deferred.reject);
            // Returns the promise - Contains result once request completes
            return deferred.promise;
        }
    }
});

Note – In this series of posts on AngularJS, I have written following post where we discussed about Angular Services. To learn more about services, you can refer that

Learning {{AngularJS}} with ASP.NET MVC – Part 4

We have added our service using Factory which uses $http service to get the data from the server. One of the key points is that we are returning a promise here. Now let’s make the required changes in the controller

After these changes, our application would run same as we are now returning the same data server via Web API. Now it’s time to write the Unit Test

Writing Unit Test

There are two new things here, usage of $http Service and returning promise. To test $http service, AngularJS provides a Fake implementation as $httpBackend which helps in mocking the service and setting up the response. To write the test, we need to initialize TalkService and httpBackend that we can inject at before each. Also we need to initialize homeModule as

var TalksServiceFactory, httpBackend;

beforeEach(module("homeModule"));

beforeEach(inject(function($httpBackend, TalksService) {
    httpBackend = $httpBackend;
    TalksServiceFactory = TalksService;
}));

And unit test

    it("Should Return four Talks", function () {
    var talks;
    
        // Setting the mock up mock http response 
        httpBackend
        .expect('GET', '/home/GetTalkDetails')
        .respond(200, [
            { id: '1001', name: 'Real Time Web Applications with SignalR', speaker: 'Brij Bhushan Mishra', venue: 'Hall 1', duration: '45' },
            { id: '1002', name: 'Power of Node.js', speaker: 'Dhananjay Kumar', venue: 'Hall 2', duration: '75' },
            { id: '1003', name: 'Getting started with AngularJS', speaker: 'Brij Bhushan Mishra', venue: 'Hall 1', duration: '60' },
            { id: '1004', name: 'Microsoft Azure - Your cloud destination', speaker: 'Gaurav mantri', venue: 'Hall 1', duration: '45' }
        ]);

        // calling service
        TalksServiceFactory.getTalks().then(function (response) {
            talks = response;
        });

        // Flushing httpBackend
        httpBackend.flush();

        // verification
        expect(talks.length).toBe(4);
    });

Above code is self-explanatory. First we are initializing the mock service, calling the service and finally verifying the response. We can configure httpBackend for different scenarios based on usage of $http in actual service.

Custom Filter

Filter is another one of the most used features of AngularJS. Here we are going to use one custom filter that we wrote in one of previous post where we discussed about Custom Filters. So let’s quickly see the Filter first

homeModule.filter('ConvertoPhone', function () {
    return function (item) {
        var temp = ("" + item).replace(/\D/g, '');
        var temparr = temp.match(/^(\d{3})(\d{3})(\d{4})$/);
        return (!temparr) ? null : "(" + temparr[1] + ") " + temparr[2] + "-" + temparr[3];
    };
});

Note – In this series of posts on AngularJS, I have written following posts where we discussed about Filters and writing custom one. To learn more about it, you can refer the following links

Now to write unit test, we need to inject the $filter and then instantiate our custom filter in the init test. Let’s see our unit test

describe("Filter Tests ->;", function () {

    var filter;
    beforeEach(module('homeModule'));

    beforeEach(inject(function (_$filter_) {
        filter = _$filter_;
    }));

    it('if the number formatted', function () {
        var phoneFilter = filter('ConvertoPhone');

       expect(phoneFilter('1234567891')).toEqual('(123) 456-7891');
    });

});

Custom Directive

Directives are again one of the most important components for AngularJS. Writing Custom Directive is a complex task because it is not just another function which can be injected and called from anywhere. Custom Directives are declaratively used in HTML. As it directly changes the view and also designed in a way to be reused at different views, provided the scope is properly isolated based on requirement, these should be properly tested.

We are going to write two Custom Directives : First would be a simple one and another using isolate scope and we will write unit test for both the cases. First directive is an element directive which reads some information from scope and replaces the directive with the provide html in directive as

homeModule.directive('myelementdirective', function () {
    var directive = {};
    directive.restrict = 'E'; //restrict this directive to elements
    directive.template = "Hello {{name}} !! Welcome to this Angular App";
    return directive;
});

Note – In this series of posts on AngularJS, I have written following posts where we discussed about writing custom directives. To learn more about it, you can refer the following links

Writing Unit Test

Writing unit test is tricky for directives because every custom directive is first complied which renders the html then other actions like binding, any user actions are performed. There is a digest cycle which runs and responsible for any binding or any other initialization before a directive appears on page.  So when writing unit test we ourself need to compile the directive using compile service and create a specific scope if required. Then run the digest cycle to make it in similar state as on UI. For that we need to initialize the scope, compile service by injecting it using beforeEach. Now let’s see our test

 var compileService, rootScope;
       
    beforeEach(module('homeModule'));

    // Store references to $compile and $rootScope so they can
    // be uses in all tests in this describe block
    beforeEach(inject(function (_$compile_, _$rootScope_) {
        compileService = _$compile_;
        rootScope = _$rootScope_;
        rootScope.talk = {
            name: 'abc', duration: '25m'
        };
        rootScope.name = 'Brij' ;
    }))

    it('My element Custom Directive defined', function () {

        var compiledDirective = compileService(angular.element('<myelementdirective/>'))(rootScope);

        rootScope.$digest();

        expect(compiledDirective).toBeDefined();

    });

Here we are compiling the directive and running the digest cycle and checking whether it is defines. Then we can write another test which checks whether the correct html is rendered or not as


    it('My element Custom Directive renders proper html', function () {

        var compiledDirective = compileService(angular.element('<myelementdirective/>'))(rootScope);

        rootScope.$digest();

        expect(compiledDirective.html()).toContain("Hello Brij !! Welcome to this Angular App");
    });
Angular App");
    });

Testing Custom Directive with isolated scope

Now we are going to write another directive with isolate scope. If you know or referred my previous post then we find that three types of isolated scope are available in Custom Directives which is also known as Local scope properties. We are going to write two way binding scope where the data is always in sync with parent regardless where it is getting changed. So let’s see the custom directive first


homeModule.directive('bindcustomdirective', function () {
    var directive = {
        restrict: 'E', // restrict this directive to elements
        scope: { talkinfo: '=' },
        template: "<input type='text' ng-model='talkinfo.name'/>" +
            "


<div>{{talkinfo.name}} : {{talkinfo.duration}}</div>



",
    };
    return directive;
});

Here talkinfo gets initialize with the scope passed via an attribute while using Directive as

 <bindcustomdirective talkdetails="talk" />

As in the template, we have input which allows to change the scope object, this reflects in the parent scope as well.

Writing Unit Test

To write the unit test, most of things would be same as above like initialization of compiler service, scope and assign some initial value to talk object in parent scope. So lets move to the test itself

 it('Bind Custom Directive defined', function () {

    var compiledDirective = compileService(angular.element(' <bindcustomdirective talkinfo="talk" />'))(rootScope);

    rootScope.$digest();

    var isolatedScope = compiledDirective.isolateScope();

    expect(isolatedScope.talkinfo).toBeDefined();
});

Here we got the compiled directive using compiler service and run the digest cycle same as earlier one. One extra line added to get the isolate scope from the compiled directive and checking whether talkInfo is defined.

We will write another test and here we will check that if we change the isolated object’s property whether that get reflected in parent scope or not as

it('Bind Custom Directive two way binding check', function () {
    var compiledDirective = compileService(angular.element(' <bindcustomdirective talkinfo="talk" />'))(rootScope);

    rootScope.$digest();

    compiledDirective.isolateScope().talkinfo.name = "Building modern web apps with ASP.NET2";

    expect(rootScope.talk.name).toEqual("Building modern web apps with ASP.NET2");
});

Conclusion

We have written the unit tests few very important components of AngularJS. Although many more test could be written and even these components vary based on requirement, accordingly different unit test may be required. But in this post, I tried to provide the basics of writing unit test for these test. Do share your feedback or face any difficulty for writing unit test for any specific component. I will try to answer that.

Cheers,
Brij

Next Post ==>
Advertisement

Getting started with Unit Testing in {{AngularJS}} – Part 19

Unit Testing one of the important activities in software development. It helps in reducing the number of bugs in the code and maintaining the code quality. As now a days we work on small releases and keep adding/updating features, Unit Test plays a vital role in maintaining the quality and helps in making sure that new changes does not break earlier functionality and reduces the testing effort. Unit Test becomes more important for languages like JavaScript because it is loosely typed and we don’t find issues until we run the functionality. Also testing and debugging JavaScript is another time consuming activity.

Note – This post is a part of series of posts on AngularJS and it is 19th one. Click here to view all the post. Apart from that you can also use the navigation links for next and previous post in each post at the bottom to check all.

What is Unit Testing

Unit Test is a snippet of code or function which tests a unit of code (function/API) and all the required dependencies are mocked. It means it just test the business logic written in the function and if any other dependent instance is required then mocked version is used. Unit test does not call any real service, database call etc.

What do we need in AngularJS to get started

Being a .NET developer, I have written thousands of unit tests using C# and Visual Studio. You might be knowing that to create and run an unit test, we require unit test framework where we can run our unit test (like NUnit, MSTest etc), Unit test APIs , Mocking framework  (like NMock, Rhino mock) and then these tests can be part of Continuous Integration system where we can run the unit tests on every check-in to the repository. Similar infrastructure set up we also need in AngularJS. There are many options but I am going to use the most recommended one’s. Let’s discuss all

  1. Jasmine – It is a behavior driven development framework for testing JavaScript code and preferred for Angular Applications. There are similar others like Qunit that we can use.
  2. Angular- mock – Angular provides its own mocking framework which helps in mocking the dependent objects.
  3. Test Runner –  One of the most used Test-runner in Karma but as we are fond of using Visual Studio, there is another nice test runner called Chutzpah which provides a plugin (Chutzpah Test Adapter) for Visual Studio which is very useful.

We have discussed the required tools. Now let’s set up our environment as

  1. Create a ASP.NET Project (I am starting with empty ASP.NET MVC Project with Unit Tests).
  2. Install Chutzpah Test Adapter plugin via Nuget manager
    chutzpah
  3. Install Jasmine Nuget package.
    Jasmine
  4. Add Angular mock (angular-mocks.js) library for mocking purposes.

Now we have set up our solution. First we will write a simple (addition) method and a unit test to verify the set up. Before writing test let’s understand the following three items which are minimum to write any unit test using Jasmine.

  1. describe – This is a global function that takes string and function as parameter which represents a suite of test. In another way, it provides us a way to group multiple tests.
  2. it – This is another function which is written inside global function and takes two parameter as above string and function and this function is actual test.
  3. expect – It takes the function that need to be tested as parameter and provide a list of matchers to match the result.

Let’s write a JavaScript  function and write unit test for that as

  1. Add a folder in scripts folder (say CustomAngular) and add new JavaScript file say Home.js .
  2. Write a Add function in the Home.js as
    function Add(firstnum, secondnum) {
        return firstnum + secondnum;
    }
    
  3. Add a file Home.test.js in scripts folder of Unit Test project to write our unit tests cases.
  4. It’s time to write our unit test for the same as
    describe("My First Test -&amp;gt; ", function () {
        it("Add with two positive num", function() {
            expect(Add(2, 3)).toEqual(5);
        });
    });
    

So we have written our first test. I already explained the special key words used here. You also need to include the references of Jasmine library and Home.js here. To run this Unit test, we just need to build the solution and open the Test Explorer and run the Unit Test. After running the Unit Test it will show green as

TestExPSampleIt means we have set up our infrastructure correctly. So let’s move to real stuff. First we will create a MVC sample application then write unit test. This application would be similar to which we have created in our series of post. But we will take small steps to understand it better. Our MVC application looks as

ExampleLet’s see code quickly. Our MVC controller (HomeController) has just one Index method which returns a view. Our Index View is as


<h2>Talk Details</h2>


<div class="container">

<div class="row" ng-controller="talkController">

<table class="table table-bordered table-condensed table-hover">

<tr ng-repeat="talk in talks">

<td> {{talk.id}}</td>


<td> {{talk.name}}</td>


<td> {{talk.speaker}}</td>


<td> {{talk.venue}}</td>


<td> {{talk.duration}}</td>

 </tr>

 </table>

 </div>

 </div>

Now let’s see our JavaScript file where we have put up our Angular code

var homeModule = angular.module("homeModule", []);

homeModule.controller("talkController", ['$scope', function ($scope) {

    $scope.talks =  [
        { id: '1001', name: 'Real Time Web Applications with SignalR', speaker: 'Brij Bhushan Mishra', venue: 'Hall 1', duration: '45' },
        { id: '1002', name: 'Power of Node.js', speaker: 'Dhananjay Kumar', venue: 'Hall 2', duration: '75' },
        { id: '1003', name: 'Getting started with AngularJS', speaker: 'Brij Bhushan Mishra', venue: 'Hall 1', duration: '60' },
        { id: '1004', name: 'Microsoft Azure - Your cloud destination', speaker: 'Gaurav mantri', venue: 'Hall 1', duration: '45' }
      ];

}]);

Also I have included Angular library, Home.js and added ng-app attribute as well. Now we will write the unit test for our Angular Controller.

First let’s understand our Angular controller. Here We have a module and a controller which takes one parameter $scope. So while writing test, we require these three items and these need to be initialized first before running the test. To initialize, Jasmine provides us beforeEach function which can be used to initialize the items which runs before the test. And we need Angular mock library to set up all. Let’s see the test and then discuss each

describe("Talk Controller Tests -> ", function () {
    var scope;
    var $ctrlCreator;

    beforeEach(module("homeModule"));
    beforeEach(inject(function ($controller, $rootScope) {
        $ctrlCreator = $controller;
        scope = $rootScope.$new();
    }));


    it("It should have four talks", function () {
        $ctrlCreator("talkController", { $scope: scope });

        expect(scope.talks.length).toBe(4);
    });

});

In the above code snippet, we first created two variables then initialized homeModule after that we injected controller and scope instance with the help of mock. In our Test, we are testing that length of talks returned by our controller is four. Now we can run our unit test via Test Explorer and it will pass. Similarly we can write many more tested for controller’s functions.

Conclusion

In this post, we talked about Unit test in angular. What are the basic things required to set up the project and get it started. Then we created a simple function Add and wrote unit test for that to check the setup. We continued writing the unit test of our controller and saw that how to initialized items before running the test. Hope you have enjoyed the post. In next Post we will continue talking about Unit Testing and write unit tests for some other components.

Cheers,
Brij