Using Karma and Grunt while testing AngularJS code with Jasmine

In my previous post, we’ve seen how to write UI test cases for AngularJS with Jasmine library. I have seen many developers learn this art of improving the quality of UI code by writing test cases, but still very few of them know the tools they are using in this process i.e. grunt, karma (Honestly, I was one of them 🙂 ).

So in this post, we are going to see how do we utilise npm package Karma and task runner Grunt in testing AngularJS code with Jasmine library.

In my previous post, we’ve took a reference of a plunk I  have created.
In the same fashion, here we are going to use a GitHub repository – SimplifyingAngularUTC I have created.

Download it in your local machine as a zip file, extract it and open the folder in your command prompt. Also open the folder in your favourite editor (Sublime text, VSCode, VisualStudio or even in notepad – it doesn’t matter).

You need to have Node.js and npm installed on your local machine to run the scripts.

As you might have noticed, the repository is almost same as per the plunk we have seen in the previous post (point 1, 2 & 3 below).

  1. scripts folder contains AngularJS module definition, controllers and services’ code
  2. vendors folder contains dependent angularjs and angular-mock libraries
  3. tests folder contains our test cases code
  4. package.json
  5. Gruntfile.js
  6. karma.config.js

The other json and js files are the new arrivals, which are actually used when we use Node Package Manager. Let’s look at them one by one.

 

  1. package.json
    • As you can see, this file contains the list of all npm packages are to be installed for the current project.
    • grunt is the JavaScript task runner we are going to use, karma & grunt-karma packages are the dependency used to run Jasmine test cases. Other packages are used to execute the test cases against browsers (remember that we are going to run UI test cases!).
  1. Gruntfile.js
    • This file is used to configure and register the grunt tasks.
    • In the initConfig call, we are telling grunt to load packages from package.json
    • We are also informing the grunt which file to use to configure karma, which in our example, is karma.conf.js
    • In the registerTask call, we have registered karma as the default task. So when we’ll execute just ‘grunt’ command, the test cases will be executed.
  2. karma.conf.js
    • In this config file, we tell the karma which files are going to be used actually to do the testing.
    • As you have seen, angularjs, angular-mocks, our source code of the application and testing related files are mentioned in ‘files‘ parameter as an array.
    • We can also mention which file we want to exclude as ‘exclude‘ parameter.
    • You can even specify on which ‘port‘ you’d like to run the test cases and also against which browser.
    • Note: once you are done installing the packages, you can also generate the karma config file yourself by karma init my.conf.js command. Have a look at the screenshot below. The command will guide you to create it. Easy it is, isn’t it!
    • creating karma config file1

In our plunk example, we were seeing the result in the browser itself. That’s why we had index.html file including angular, mock and jasmine files; and browsing index.html file we are able to see the result (now you’ll relate how karma config is helping us to do the same here).

Now, follow the instructions mentioned in the repository README file and execute the commands in the command prompt.

  1. npm install (installs the npm packages specified in package.json)
  2. npm install -g grunt-cli (installs grunt cli; -g for globally)
  3. npm install -g karma-cli (installs karma cli; -g for globally)

The above commands install…

  1. the packages mentioned in package.json file
  2. grunt-cli and karma-cli packages (to use the command grunt directly from the command prompt)

Once done, execute command ‘grunt‘. You’ll see the result in the command prompt as below. You might need to terminate the process with ctrl+c after they are executed.

As you can see that all the fifteen test cases are executed.

Running grunt command to run test cases

Now, if you want to execute test cases for just a single file, (for example, if you want to do it just for myMathSvcSpec.js file) go to the spec file, add ‘d‘ in front of the ‘describe‘ as per below, save it, run grunt command and you can see that only those test cases are executed which are there in that spec file. The rest are skipped.

Running grunt command to run test cases-skipping other files

If you want to exclude some files while executing the test cases, you can mention in the karma.conf.js file. For example, if you don’t want test cases of myMathSvcSpec.js to be executed along with the others, you can mention it is karma config file as ‘exclude’ property. Have a look at the screenshot below. As you can see, five test cases of myMathSvcSpec are not executed.

Running grunt command to run test cases-skipping specific file

Dexters-Laboratory-PNG-Pic

Hope you had a good learning for the topic reading this post.

How do you like it? Any suggestion, question, or anything you would to say about this.

Let me know in the comments section below.

Advertisements

Unit testing AngularJS with Jasmine

As a developer, I was running away from writing unit tests of UI. However, it is important to develop practice to write the UI unit tests. There are many reasons for that and we are not going to go in that direction, so let’s come to the point.

In this blog, we are going to learn how to write unit test cases for AngularJS controllers with the help of Jasmine library.

Prerequisites:

  1. AngularJS
  2. Some details regarding why we write unit test cases would help

The example I have used uses Angular 1.2.11 and Jasmine 1.3.1. With minor changes, this should also work for the latest version of AngularJS, so you can just focus on the concepts.

Other points to consider:

  • For Jasmine 2.x, there are few changes in some methods.
    For example, for Jasmine 1.3, you would write spyOn(mockObj, ‘method’).andReturn(someReturnVal)
    The same thing for Jasmine 2.x, you would write as spyOn(mockObj, ‘method’).and.return(someReturnVal)
    Also, there are some changes/enhancement in 2.x, but as of now, we will just focus on the basics.
  • You all might be aware that Angular 2 (or 4+) is going to use TypeScript; if you are going to write unit test cases for Angular 2 (4+) using Jasmine, there will be just change in the syntax of the test case. The concept would just remain the same. For example (given in https://docs.angularjs.org),
describe('sorting the list of users', function() {
  it('sorts in descending order by default', function() {
  var users = ['jack', 'igor', 'jeff'];
  var sorted = sortUsers(users);
  expect(sorted).toEqual(['jeff', 'jack', 'igor']);
 });
});

The same thing you would write in TypeScript as per below.

describe('sorting the list of users', () => {
  it('sorts in descending order by default', () => {
    var users = ['jack', 'igor', 'jeff'];
    var sorted = sortUsers(users);
    expect(sorted).toEqual(['jeff', 'jack', 'igor']);
  });
});

In short, the concepts you are going to learn in this blog are also going to be applicable for the latest version of Angular as well as Jasmine.

 

I have created a plunk, which we are going to use as a reference.

link for the plunk - Unit Testing AngularJS with Jasmine

click on this link to open it in new browser window/tab.

Now let’s start understanding how to write unit test cases. Keep your eyes on myMathControllerSpec.js.

If you observe at bird’s eye view, you can identify that the UTC file consist of…..

  1. one describe section/function call at root level
  2. beforeEach section(s)/call(s)
    • there is an inject section/call within beforeEach where we get/create the instances of the dependencies
  3. it section/call where we write and execute individual test cases

Based on the complexity, we may also have (describe)* (0 or multiple describe) section and within that describe, we have (it)+ (one or multiple it) section.

You will also observe that we are…

  1. Initially loading the current and dependant external module
  2. Load/create external dependencies to instantiate the controller
  3. Inject the dependencies and create controller instance
  4. Prepare the data and set spies (check in the test case #2)
  5. Call the function and test the expected result

If we are going to test myApp, first of all we need to load the module in the memory. This is needed when we get the controller instance to test. We may also need to load mySvcApp (in the definition of myApp, verify that mySvcApp is injected as dependency).You may directly inject those dependencies which you get using $injector or you may write mock objects. Verify that we are just giving mock objects of window, log and myMathSvc.

It’s better to provide examples than explaining the same in thousands of words!

So here I am going to provide you the quick references and examples for what kind of unit test cases you can write with Jasmine. All the references belong to the plunk I have created.

Now check myMathController.js and its Spec file – specially test case #2. Below things you will see and learn there.

  1. How to set ‘spyOn’ for a service method and returning a fake result
  2. How to expect the service method to get called
  3. How to expect the service method to get called with specific arguments
  4. How to expect the result as a controller $scope property
  5. How to check if the method is called single or multiple times
  6. How to get the details about just the most recent call of a service
  7. How to reset the spy set on a method (setting isSpy = false)
  8. How to expect and test a function throwing exception/error.
    • You’ll simply set spyOn a method and throw error in this way
      spyOn(myMathSvc, 'divide').andThrow(new Error(errorMsg));
  9. How to test the controller if we are not using $scope level properties (using this inside controller)
    • In this type of controller, we use this keyword extensively.  You’ll find the example in myThisControllerSpec.js. Have a look at the controller before moving ahead!
  10. How to test $watch callback on a controller property.
    • Many times we keep $watch on a controller property and do some operations when it changes. Example is explained in myHttpController and testing for the same is provided in related Spec file – test case #4.
  11. How to test $viewContentLoaded callback function
    • In some cases, we want to call data load operation or any other thing once we are sure that the view is loaded into the DOM. As you know, $viewContentLoaded event is called when this happens. Example of it is given in myHttpController ($scope.getData()) and testing for the same is provided in related Spec file – test case #3.
    • Here we need to load the controller in HTML markup template and compile it against the scope. i.e.
      var viewTemplate = 'divStart ng-controller="myHttpController" divEnd';
      //consider the appropriate tags in the above statement
      $compile(viewTemplate)(scope);
      scope.$digest();
  12. How to test an async service call – or – a service call returning promise.
    • This is very well explained in test case #2 of myHttpController. Have a look at how getServerData is called within the controller first of all.
    • Don’t miss to learn from the comment I have provided over there – then/finally part!!

Apart from all these, we also have test cases for custom directives, scenarios where jQuery code is used (ideally this should be avoided while working with AngularJS, but we have also got it covered!), i.e. jQuery methods like find, animate, index, etc.

I will be covering them next time. Till then, happy learning!

Dexter

 

If you think something more should also be added to this blog, any improvement points, or anything you like about this, do comment. Your feedback is welcomed!