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

Creating custom directive in AngularJS – Part 9

In this post, we’ll discuss how to create custom directive. This is a big topic so I will be discussing in coming couple of posts.As a whole, this is ninth post in series of Learning AngularJS and I hope you are enjoying this series. The link of my earlier posts in the series are below

Learning {{AngularJS}} with Examples–Part 1

Learning {{AngularJS}} with Examples–Part 2

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

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

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

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

DataBinding in {{AngularJS}} – Part 7

Data Binding in {{AngularJS}} : Under the hood – Part 8

So let’s come to topic and start from the basics.

What are Directives?

From Angular’s documentation “At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS’s HTML compiler ($compile) to attach a specified behavior to that DOM element or even transform the DOM element and its children.

So anything that we write in HTML that is handled by Angular are Directives like {{}}, ng-click, ng-repeat etc.

How Directives are handled?

Directives provides us a capability to have more control and flexible way to rendering the html. As we know these are not standard HTML attribute so browser doesn’t understand it. Angular chips in between and reads all the directives and process them to emit the browser readable HTML. So what is the process of handling directives? I already discussed that in my last post, but I am including it here briefly.

ngCompilingProcessSo there are two steps involved here: Compiling and Linking.

Type of Directives

There are four types of directives. These are

  • Element directives
  • Attribute directives
  • CSS class directives
  • Comment directives

Element Directives – Element directives are like HTML elements as

<myElementDirective></myElementDirective>

Attribute Directive – Attribute directives are which can be added an attribute over an element as

<div myAttrDirective></div>

CSS class directives– These directive can be added as an CSS class

<div class="myAttrDirective: expression;"></div>

Comment Directives– Comment directives is represented as

<!-- directive: myCustomAttrDirective expression -->

How to create a custom directive

One of the key benefits of AngularJS is that apart from its built-in directives, it allows us to write our own custom directives so that we can render the HTML on browsers based on our specific requirement. Angular provides us a simple syntax to create our own custom directive.

var myElementDirective = function () {
var myDirective = {};

myDirective.restrict: 'E', //E = element, A = attribute, C = class, M = comment
myDirective.scope: {
// To provide scope specific for that directive  },
myDirective.template: '&lt;mdiv&gt;This is custom directive&lt;/div&gt;',
myDirective.templateUrl: '/myElementTemplate.html',
// use either template or templateUrl to provide the html
myDirective.controller: controllerFunction, //custom controller for that directive
myDirective.link: function ($scope, element, attrs) { } //DOM manipulation
}

The brief description of each property as

restrict Defines the type of directive. Possible values are E (element), A (Attribute), C (Class), M (Comment) or any combination of these four
scope Allows us to provide a specific scope for the element or child scopes
template This contains the HTML content that would be replaced in place of directive. It also could contain any other angular directive
templateUrl Allows us to provide the URL of the HTML template that contains the actual html content similar to above. Set any one of the two.
controller controller function for the directive

So we got the details about creating a custom directive. But the next question is –  How to register it? As we discussed on one my earlier posts (part-2) that Module works as container in AngularJS so the same applies here as well. It means we need to register it with module as directive. We’ll see that coming example.

Let’s create an example

So I am going to create a simple element directive as

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

    myangularapp.directive('myelementdirective', function () {
            var directive = {};
            directive.restrict = 'E'; //restrict this directive to elements
            directive.template = "Hello World using Custom Directive";
            return directive;
        });

and HTML is

<body ng-app="myangularapp">
    <myelementdirective></myelementdirective>
</body>

No when we run this page, it will appear as

basicdirectiveexSo here we can see the element myelementdirective is replaced with the text. In this example, I have used only few properties that I discussed.

So here we can see that this is an element directive as restrict is set to E (Element). restrict property can be also set as any combinations of E,A,C,M like EA or EAC. If we set it as EA then compiler will find myelementdirective as an element or attribute and replace it accordingly.

We can also use templateUrl instead of template.  templateUrl will pick the content of the file and use it as template.

Now, Let’s understand the flow of it working

1- When the application loads, Angular Directive is called which registers the directive as defined.

2- Compiler find out any element with the name myelementdirective appears in the HTML.Here, It finds at one place.

3- Replaces that with the template.

So looks simple. Right!! Let’s see another example

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

So here we used an interpolate directive {{}} in the template and used name. So we need to provide the value for this. So let’s add a controller to handle that

    myangularapp.controller("mycontroller", function ($scope) {
        $scope.name = 'Brij';
    });

Now when we run the application, Would it work?

directiveintrapolateSo it did work. But in this case, what would be the flow?

As I mentioned earlier that there are steps involved while rendering a page. Compiling and linking. All the data population takes place in linking phase. So during compilation phase it replaced the directive and in linking phase the data got populated from the scope. So earlier steps would be there, just one more steps would be inserted in between 2nd and 3rd.

Using Transclude

This word may look complex but it provides a very useful feature. Till now we have seen that custom directive are just replaced by the template that we provide while creating custom directive but there could be some scenario where we donot want to replace the whole html but only some part of it. Like say I want that the text that is wrapped by the custom directive should not get replaced but only element part. Didn’t get? Let’s see pictorially

Say I created a custom element directive  as myCustomDirective and the template provided for it as <div>Some custom template text<div>
WithoutTransclude

 

 

In case of transclude

WithTransclude

 

 

 

Note – Here I changed the template as <div ng-transclude>Some custom template text</div> to get the transclusion working.

So in the above pic, we can see that the text is not replaced while in case normal directive the whole template replaces the directive.

Now we understood the transclusion so let us see what all we need to do to use it. There are two steps

1- Set the transclude property to true while creating directive.

2- We need to inform Angular that which part contains the transcluded html that’s why for transclusion example, I changed the template a bit (Refer Note above)

Hope you have enjoyed this post. In the next post, we’ll some more interesting feature of custom directives. Stay tuned.

Happy Learning
Brij