AngularJS From Beginning: Unit Test Of AngularJS Controller - Part Thirteen

I am here to continue the discussion around AngularJS. Today, we will discuss how to perform unit test of AngularJS controller using Jasmine. Also in case you have not had a look at our previous articles of this series, go through the following links:

In this article, we will discuss how to perform the unit testing with the angularjs code.

Why and When to Unit Testing

Nowadays, JavaScript has become the de facto programming language to build and empower frontend/ web applications. We can use JavaScript to develop simple or complex applications. However, applications in production are often vulnerable to bugs caused by design inconsistencies, logical implementation errors, and similar issues. For this reason, it is usually difficult to predict how applications will behave in real-time environments, which leads to unexpected behavior, non-availability of applications, or outages for short or long durations. This generates lack of confidence and dissatisfaction among application users. Also, high cost is often associated with fixing the production bugs. Therefore, there is a need to develop applications that are of a high quality and that offer high availability.

Unit testing is the technique of isolating a single small piece of functionality and testing it independently of the rest of the application and AngularJS. Carefully applied, unit testing can reduce the number of software defects that show up later in the development process, especially those that the user encounters when the application is deployed. Unit testing works best with teams that have strong design skills and a good understanding of what and who the finished product is for. Without those skills and the broader perspective, the narrow focus that unit testing creates can put too much emphasis on the quality of individual bricks at the cost of the overall structure of the house they are being used to build. The worst possible environment for unit testing is the one in which I encounter it most often: large corporate projects with thousands of developers. In these projects, individual developers have little visibility of the overarching objectives in anything other than the broadest terms, and passing arbitrary unit tests quickly becomes the sole measure of quality, which requires developers to make assumptions about external inputs to their code that turn out to be wrong. In these situations, a project whose unit test results are positive will get bogged down in integration testing as all of those individual assumptions are discovered and found wanting. Even so, unit testing can be a powerful tool when applied carefully. Just be sure that you are able to measure the benefit that it brings, that you understand that unit testing triggers the natural inclination of many developers to turn their focus inward, and that passing unit tests doesn’t mean that those units are going to work well together. Use unit testing as part of a broader strategy of end-to-end testing.

In today’s world, there are several tools for perform unit test on code for any JavaScript based framework line AngularJS. Out of those, I always preferred Jasmine as the most popular and stable tool for doing this task. Actually Jasmine is a behavior driven development framework for JavaScript. But at the same time, it can be used to write a unit test with a test driven development (TDD) approach.

How to Install Jasmine on Windows

For installing Jasmine on Windows machine, we need to first install node.js. For this, we need to download the node.js msi installer for windows operating systems from the link https://nodejs.org/. Now after download the msi file, install the msi file.

Steps of Install NodeJs

 
 
 
 
 
 
 
 
After completing the node.js installation, we need to open the command prompt of node.js to check either npm (node’s package manager) successfully installed or not. Actually for jasmine, we need some other modules like Yeoman, Brower, Generator-Jasmine which we install using npm. For this write down the below command in sequence in npm.

Step 1

npm -- version (for checking npm is properly installed or not)

 
Step 2

npm install –g yo (for install yomen)


Step 3

npm install –g bower (for install bower)

cmd

Step 4

npm install –g generator-jasmine jasmine-core (for install jasmine)



The –g switch asked npm to install packages within node.js global modules directory. For finalize testing environment, we need to scaffold Jasmine’s Test directory. For this go to the your project location path and then run the below command. This command create a test directive and within that directive create a index.html file and a spec folder for storing the spec related js file within that directive. The index.html file is test runner or spec runner file of the jasmine.

Step 5

yo jasmine,

 

There is another way of using jasmine. For it, we can install karma framework using npm by using the below command.

npm install –g karma karma-cli jasmine-core

Now after it, go the project directory by browsing and initialize the configuration. For this karma ask few simple questions and on the basis of given answer it will create the configuration files.

karma init jasmine.config.js

 

After completing the configuration file creation, we just need to start the karma by passing the configuration file name as arguments.

karma start jasmine.config.js

Now, the environment for runt the jasmine test has been ready. Now, need to run the jasmime test. For this, We create a folder named test within the project directive and add one html file called index.html for running the jasmine test file. Now first we will create a simple JavaScript function which takes two number arguments and return the total of two numbers. For this, we need to add another JavaScript file within the same folder named sampletest.js and write down the below code –

Also, we need to another JavaScript file for write down the code of unit test. For this, we add another js file test.js and write down the below code. In this file, we add two scenarios. One for correct scenario and another is wrong scenario i.e. expect and given data does not match.

Now we add the below code in the index.html file.

When I write unit tests, I follow a pattern called arrange/act/assert (A/A/A). Arrange refers to the process of setting up the scenario required for the test. Act refers to performing the test itself, and assert refers to checking the result to make sure it is correct. Jasmine tests are written using JavaScript functions, which makes writing tests a nice extension of writing application code. There are several Jasmine functions in the example, which I have described below.

NameDescriptions
describeGroups a number of related tests (this is optional, but it helpsorganize test code)
beforeEachExecutes a function before each test (this is often used for thearrange part of a test)
itExecutes a function to form a test (the act part of the test)
expectIdentifies the result from the test (part of the assert stage)
toEqualCompares the result from the test to the expected value (the otherpart of the assert)

The basic sequence to pay attention to is that the it function executes a test function so that the expect and toEqual functions can be used to assess the result. The toEqual function is only one way that Jasmine can evaluate the result of a test. I have listed the other available functions as below. 

NameDescriptions
expect(x).toEqual(val)Asserts that x has the same value as val (but not necessarily the same object)
expect(x).toBe(obj)Asserts that x and obj are the same object
expect(x).toMatch(regexp)Asserts that x matches the specified regular expression
expect(x).toBeDefined()Asserts that x has been defined
expect(x).toBeUndefined()Asserts that x has not been defined
expect(x).toBeNull()Asserts that x is null
expect(x).toBeTruthy()Asserts that x is true or evaluates to true
expect(x).toBeFalsy()Asserts that x is false or evaluates to false
expect(x).toContain(y)Asserts that x is a string that contains y
expect(x).toBeGreaterThan(y)Asserts that x is greater than y

For perform a simple unit test, I first write down a simple JavaScript function which add two numbers :-

  1. function addNumber(x, y) {  
  2.     return x + y;  
  3. }  
Now, I add a spec file called index.html and write down the below code,
  1. <!doctype html>  
  2. <html>  
  3. <head>  
  4.     <title>Jasmine Spec Runner</title>  
  5.     <link href="../test_resource/jasmine.css" rel="stylesheet" />  
  6. </head>  
  7. <body>  
  8.     <script src="../test_resource/jasmine.js"></script>  
  9.     <script src="../test_resource/jasmine-html.js"></script>  
  10.     <script src="../test_resource/boot.js"></script>  
  11.     <script src="../CodeJs/sampleTest.js"></script>  
  12.   
  13.     <script src="spec/test.js"></script>  
  14. </body>  
  15. </html>  
Now, I will add the another js file to write down the unit test code as below,
  1. (function () {  
  2.     'use strict';  
  3.   
  4.     describe('addNumber Correct'function () {  
  5.         it('should return values'function () {  
  6.             var x = 10;  
  7.             var y = 20;  
  8.             var z = x + y;  
  9.             expect(addNumber(x, y)).toEqual(z);  
  10.         });  
  11.     });  
  12.   
  13.     describe('addNumber Incorrect'function () {  
  14.         it('should return Incorrect values'function () {  
  15.             var x = 1;  
  16.             var y = 20;  
  17.             var z = x + y;  
  18.             expect(addNumber(x, y)).toEqual(z);  
  19.         });  
  20.     });  
  21.   
  22. })();  
Now, as per the above code, I create two test scenario. Now when I run the spec html file, the below output comes,

Unit Test of a AngularJS Controller using ngMocks

AngularJS provides an optional module called ngMock, which provides useful tools for unit testing. Go to http://angularjs.org, click Download, select the version you require and click the download button. Now, add this file into your project directive script folder and also take the file reference into the index.html file. We can also download this file using npm with command line as per below command npm install angular-mocks.

Understanding the Mock Objects

Mocking is the process of creating objects that replace the key components in an application to allow effective unit testing. Imagine that you need to test a controller behavior that makes an Ajax request using the $http service. The behavior depends on a number other components and systems: the AngularJS module to which the controller belongs, the $http service, the server that processes the request, the database that contains the data that is being asked for, and so on. When the test fails, you won’t know whether the problem is caused by the controller behavior you are trying to test or because of an unrelated fault; perhaps the server has crashed or can’t connect to the database, for example. The components that the target for testing depends on are replaced with mock objects, which implement the API of the components that are required but generate fake, predictable results. You alter the behavior of the mock objects to create different scenarios in which to test your code, which makes it easy to arrange a wide range of tests without having to endlessly reconfigure test servers, databases, networks, and so on.

The Test Objects and APIs

In this section I am going to list the mock objects and some additional features that AngularJS provides to make testing easier. I’ll then use these in the rest of the article so that I can explain how they are used to create focused and effective unit tests. The ngMocks module contains a set of mock objects that are used to replace AngularJS components, as below. 

NameDescriptions
angular.mockUsed to create mock modules and resolve dependencies.
$exceptionHandlerA mock implementation of the $exceptionHandler service that rethrows the exceptions
it receives.
$intervalA mock implementation of the $interval service that allows time to be moved forward to trigger scheduled functions on demand.
$logA mock implementation of the $log service that exposes the messages it receives through a set of properties, one for each of the methods defined by the real service.
$timeoutA mock implementation of the $timeout service that allows the timer to be expired programmatically so that the associated function executed on demand.

The angular.mock object provides methods that load modules and allows dependencies to be resolved in unit tests. I have described the available methods as below.

NameDescriptions
module(name)Loads the specified module. See the “Arranging the Test” section.
inject(fn)Resolves dependencies and injects them into a function
dump(object)Serializes an AngularJS object (such as a service object).
$rootScope.new()Creates a new scope
$controller(name)Creates an instance of the specified controller
$filter(name)Creates an instance of the specified filter

Now, we add another js file named calculateController.js file and write down the controller file with four different method which takes two number as input from html page and perform add, subtract, multiply and divide process and return the respective results.

 CalculateController.js
  1. testApp.controller('calculateController'function ($scope) {  
  2.     $scope.FirstNum = 0;  
  3.     $scope.SecondNum = 0;  
  4.   
  5.     $scope.add = function () {  
  6.         return $scope.FirstNum + $scope.SecondNum;  
  7.     }  
  8.   
  9. });  
Index.html
  1. <!doctype html>  
  2. <html>  
  3. <head>  
  4.     <title>Jasmine Spec Runner</title>  
  5.     <link href="../test_resource/jasmine.css" rel="stylesheet" />  
  6. </head>  
  7. <body>  
  8.     <script src="../test_resource/jasmine.js"></script>  
  9.     <script src="../test_resource/jasmine-html.js"></script>  
  10.     <script src="../test_resource/boot.js"></script>  
  11.    
  12.     <script src="../../../RefScript/angular.min.js"></script>  
  13.     <script src="../test_resource/angular-mocks.js"></script>  
  14.     <script src="../CodeJs/testApp.js"></script>  
  15.     <script src="../CodeJs/calculateController.js"></script>  
  16.   
  17.     <script src="spec/test.js"></script>  
  18. </body>  
  19. </html>  
test.js
  1. (function () {  
  2.     'use strict';  
  3.   
  4.     describe('Angular JS Controller Test'function () {  
  5.         var $mockScope = {};  
  6.         var $controller;  
  7.   
  8.         beforeEach(angular.mock.module("TestApp"));  
  9.   
  10.         beforeEach(angular.mock.inject(function ($controller, $rootScope) {  
  11.             $mockScope = $rootScope.$new();  
  12.             $controller = $controller("calculateController", {  
  13.                 $scope: $mockScope  
  14.             });  
  15.         }));  
  16.   
  17.   
  18.         it('add method'function () {  
  19.             $mockScope.FirstNum = 10;  
  20.             $mockScope.SecondNum = 15;  
  21.             expect($mockScope.add()).toEqual(25);  
  22.         });  
  23.     });     
  24.   
  25. })();  

I need two things to perform this test: an instance of the controller and a scope to pass to its factory function. To reach that point, I have to do some preparation. The first step is to load the module that contains the controller, which I do using beforeEach function of jasmine. As you have seen throughout this article series, dependency injection is an important part of how AngularJS works, and that means unit tests need to be able to resolve dependencies in order to function. The angular.mock.inject method will resolve the dependencies on the function it is passed, and this provides access to the services required for the test. The function I passed to the inject method declares dependencies on the $controller and $rootScope services. In general, the inject method is used to arrange the unit test, and the function that it is passed sets up test variables that will later be used in Jasmine it calls. My goal in the function shown earlier is to create a new scope and pass it to an instance of the controller in the example application so that it can define its behavior and data.

The $rootScope service defines a $new method that creates a new scope, and the $controller service is a function used to instantiate controller objects. The arguments to the $controller service function are the name of the controller and an object whose properties will be used to resolve the dependencies declared by the controller’s factory function. My simple controller requires only a scope for its factory function, but more complex controllers will require other services (which you can obtain through the inject method). By the time that the function passed to the inject method has completed, the controller will have been instantiated, and its factory function will have operated on the scope I created. I assigned the scope object to a variable called $mockScope, which I can then use in the act and assert phases of the test.

Now within the Jasmine it function, I defined the scope value and then call the add() using expect function and then assert the return result with the expected value. Now just open the index.html file in browser and see the output.

In this article, I discuss how to perform unit tests using Jasmine for simple JavaScript functions and a simple AngularJS controller file. In the next article, I will discuss about how to perform the unit test with ajax requests including directive and service tests.