AngularJS Unit Testing With Jasmine

Introduction

AngularJS always promotes and encourages the automated testing therefore the tests for the AngularJS code has been written in Jasmine. Jasmine is a framework for the testing of JavaScript code. Its a Behavior Driven Development (BDD) for testing JavaScript code. Basically, BDD is a software development process that has driven from Test Driven Development (TDD). AngularJS provides Jasmine extensions to developers for the best unit testing experience.
 
Karma supports so many testing frameworks. So, Jasmine is the default option for Karma. I will explain the concept to write unit test with the help of Jasmine framework.

describe function

It is a global function that is being defined in the Jasmine unit test. It takes two parameters - a string and a function.
  1. String: It is the name or description of the spec suit.
  2. Function: It encapsulates the unit test specs. 
Example
  1. describe('Unit Test: MyController'function() {  
  2. });  

Define a Spec

Specs in Jasmine is being defined by it() function.
 
The it() function takes two arguments:
  1. title: It is a string title and a title or description of the specs.
  2. function: It contains one or more expectations. These expectations are basically functions. It returns true or false after execution.
Example
  1. describe('my spec desc', function()  
  2. {  
  3.   
  4.     it('spec definition', function()  
  5.     {  
  6.         expect(true).toBe(true);  
  7.     });  
  8. });  

Expectations

Expectation is a case in which developer expects some value or result at different stages of the application.
Expectation is being set using the expect() function. It takes an argument that is called actual value. So, it compares between the actual value and the expected value.
 
Example
  1. describe('my spec desc', function()  
  2. {  
  3.   
  4.     it('my spec definition', function()   
  5.     {  
  6.         expect(true).toBe(true);  
  7.     });  
  8.   
  9.     it('my second spec definition', function()  
  10.     {  
  11.         expect(false).not.toBe(true);  
  12.     });  
  13. });  

matchers

toBe

It compares values like JavaScript operator ===.
 
Example
  1. describe('my spec description', function()  
  2.     {  
  3.     it('expectation description ', function()  
  4.        {  
  5.         var value = 10,  
  6.             another_value = value;  
  7.         expect(value).toBe(another_value);  
  8.         expect(value).not.toBe(null);  
  9.     });  
  10. });  

toEqual

It compares values and works for simple literals and variables.
 
Example
  1. describe('my spec desc', function()   
  2.     {  
  3.     it('my spec expectation', function()   
  4.        {  
  5.         var value = 10;  
  6.         expect(value).toEqual(10);  
  7.     });  
  8. });  

Jasmine has so many other matchers like the following:

  1. toBeDefined
  2. toBeUndefined
  3. toMatch
  4. toBeNull
  5. toBeTruty
  6. toBeFalsy
  7. toContain
  8. toBeLessThan
  9. toBeGraterThan

Unit Test Spec Explanation

Now, I will explain one basic Jasmine Unit Test spec related to an asynchronous service call that returns a promise.

Controller and Service: app.js
  1. //Module    
  2. var app = angular.module('myModule', []);  
  3. //Controller -- It is calling myService.    
  4. app.controller('MainCtrl', function($scope, myService)   
  5. {  
  6.     $scope.name = "Testing";  
  7.     $scope.temp = myService.myServiceData();  
  8. });  
  9. //Service    
  10. app.service('myService', function($q)  
  11. {  
  12.     this.myServiceData = function()   
  13.     {  
  14.         var deferred = $q.defer();  
  15.         return deferred.promise;  
  16.     }  
  17. });  
Jasmine Unit Test Spec : appSpec.js
  1. describe('Unit Testing: MainCtrl', function()  
  2. {  
  3.     var $scope = null;  
  4.     var ctrl = null;  
  5.     var mockMyService;  
  6.     var deferred;  
  7.     var $q;  
  8.     var data =  
  9.     {  
  10.         name: 'test'  
  11.     };  
  12.     var createController;  
  13.     beforeEach(module('myModule'));  
  14.     beforeEach(inject(function($rootScope, $controller, $q)  
  15.         {  
  16.         $scope = $rootScope.$new();  
  17.         $q = $q;  
  18.         mockMyService =  
  19.         {  
  20.             myServiceData: function() {}  
  21.         };  
  22.         deferred = $q.defer();  
  23.         spyOn(mockMyService, 'myServiceData').and.returnValue(deferred.promise);  
  24.         createController = function() {  
  25.             return $controller('MainCtrl',   
  26.             {  
  27.                 $scope: $scope,  
  28.                 myService: mockMyService  
  29.             });  
  30.         };  
  31.     }));  
  32.     it('Should call myServiceData()', function()  
  33.        {  
  34.         var resolvedValue;  
  35.         ctrl = createController();  
  36.         $scope.temp.then(function(value)  
  37.         {  
  38.             resolvedValue = value;  
  39.         });  
  40.         deferred.resolve(data);  
  41.         $scope.$digest();  
  42.         expect(mockMyService.myServiceData).toHaveBeenCalled();  
  43.         expect(resolvedValue).toEqual(data);  
  44.     });  
  45. });  
JasmineBootStrap.js

It will create the `HTMLReporter`, which Jasmine calls to provide results of each spec and each suite. The Reporter and the spec result to the user.
  1. (function()   
  2.  {  
  3.     var jasmineEnv = jasmine.getEnv();  
  4.     jasmineEnv.updateInterval = 250;  
  5.     var htmlReporter = new jasmine.HtmlReporter();  
  6.     jasmineEnv.addReporter(htmlReporter);  
  7.     jasmineEnv.specFilter = function(spec)   
  8.     {  
  9.         return htmlReporter.specFilter(spec);  
  10.     };  
  11.     var currentWindowOnload = window.onload;  
  12.     window.onload = function()   
  13.     {  
  14.         if (currentWindowOnload)   
  15.         {  
  16.             currentWindowOnload();  
  17.         }  
  18.         execJasmine();  
  19.     };  
  20.   
  21.     function execJasmine()   
  22.     {  
  23.         jasmineEnv.execute();  
  24.     }  
  25. })();  

style.css:

It is just for the reporting of Jasmine Unit test report.
  1. body  
  2. {  
  3.     background - color: skyblue;  
  4.     padding: 0;  
  5.     margin: 8 px;  
  6. }  
  7. .jasmine_reporter  
  8. {  
  9.     background - color: #0094ff;    
  10.     padding: 0;    
  11.     margin: 0;    
  12. }  
index.html

It is the display html page for the spec report.
  1. <!DOCTYPE html>  
  2. <html ng-app="myModule">  
  3.   
  4. <head>  
  5.     <meta charset="utf-8" />  
  6.     <title>AngularJS Unit Test with Jasmine!</title>  
  7.     <link data-require="jasmine" data-semver="2.0.0" rel="stylesheet" href="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine.css" />  
  8.     <script data-require="json2" data-semver="0.0.2012100-8" src="//cdnjs.cloudflare.com/ajax/libs/json2/20121008/json2.js"></script>  
  9.     <script data-require="jasmine" data-semver="2.0.0" src="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine.js"></script>  
  10.     <script data-require="jasmine" data-semver="2.0.0" src="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine-html.js"></script>  
  11.     <script data-require="jasmine@*" data-semver="2.0.0" src="//cdn.jsdelivr.net/jasmine/2.0.0/boot.js"></script>  
  12.     <script data-require="angular.js" data-semver="1.2.16" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script>  
  13.     <script data-require="angular-mocks" data-semver="1.2.16" src="https://code.angularjs.org/1.2.16/angular-mocks.js"></script>  
  14.     <link rel="stylesheet" href="style.css" />  
  15.     <script src="app.js"></script>  
  16.     <script src="appSpec.js"></script>  
  17.     <script src="jasmineBootstrap.js"></script>  
  18. </head>  
  19.   
  20. <body>  
  21.     <div id="container" ng-controller="MainCtrl">  
  22.         <h1>Unit Testing of a Service that Returns a Promise - Jasmine {{name}}</h1>  
  23.     </div>  
  24.     <div id="HTMLReporter" class="jasmine_reporter"></div>  
  25. </body>  
  26.   
  27. </html>  
Report



Conclusion

AngularJS community always promotes and encourage unit testing practice. So, they have written and enhanced framework in such way that it makes easy for the developer. And, Jasmine is a very good framework for the unit test. See the attached code.