Unit testing AngularJs with Jasmine

How to setup Karma and Jasmine to test AngularJs Controllers and Services in the IDE WebStorm.

Link to the example project:
https://github.com/ckirschberg/AngularUnitTestWithJasmine

This tutorial is a reminder for myself but if you use WebStorm you might find it helpful. It was created using a Mac. Omit sudo from the following commands if you are on Windows.

Run the following commands from the terminal window in the WebStorm IDE.
I any of these commands runs "forever", press Ctrl+C, and try again.

Create new project

(You can omit this part and jump to "Install Karma and Jasmine" if you already have a project).

Create a folder for the project and open that folder in WebStorm.
From the terminal window, run the following commands:

Create package.json with:

npm init  

Press enter to all questions and it will generate the following package.json file:

{
  "name": "jasminetest",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Install karma and jasmine:

sudo npm install -g karma-cli  
sudo npm install karma --save-dev  
sudo npm install -g karma-jasmine --save-dev  
sudo npm install karma-chrome-launcher --save-dev  
sudo npm install karma-firefox-launcher --save-dev  
sudo npm install karma-safari-launcher --save-dev  
sudo npm install -g generator-jasmine  

Create the karma.conf file:

karma init karma.conf.js  

press enter to all questions, except:

Do you want Karma to watch all the files and run the tests on change ? Press tab to list possible options.
no

You will see the following file called karma.conf.js in your project.

module.exports = function(config) {  
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',


    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],


    // list of files / patterns to load in the browser
    files: [
    ],


    // list of files to exclude
    exclude: [
    ],


    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },


    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],


    // web server port
    port: 9876,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,


    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],


    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};

Now run the following, to create a test library:

yo jasmine  

This command will generate a folder called test in your project.
Inside the test folder, there is a folder called spec. This is where we will put our test files.

In the WebStorm IDE, right click the file called karma.conf.js and select Create 'karma.conf.js' in WebStorm. Select Karma package: /usr/local/lib/node_modules/karma
(It did not work before I changed to this).

A green arrow button appears in the top right corner, along with karma.conf.js. Click the green arrow button to run our tests (for later).

Next, we configure which files we want to load and where our test-files are located by changing the karma.conf.js file.

I want to load angular.js, angular-mocks.js, my .js files, in this case controllers.js and services.js, and the test-files in the spec-folder, so I write the following in the files-array (in the karma.conf.js file):

files: [  
      './node_modules/angular/angular.js',
      './node_modules/angular-mocks/angular-mocks.js',
      './app/js/controllers.js',
      './app/js/services.js',
      './test/spec/*.js'
    ],

This is a list of files that you need to perform your tests, libraries and app related files.

Next, we install AngularJs using npm by running:

sudo npm install angular  

Then, we install Angular mocks using npm by running:

sudo npm install angular-mocks  

I created a simple Angular controller and -service and I'll demonstrate how to write a simple test for each of these.

I started out by creating a folder in my project called app and another folder inside the app folder, called js. In this folder, I created a file called controllers.js with the following code:

var personController = angular.module('personController', []);

personController.controller('pController', function ($scope) {  
    $scope.people = [
        {'name':       'Jens Jensen'},
        {'name':       'Hans Hansen'},
        {'name':       'Per Persen'}
    ];
});

The Angular service (in a file called services.js in the same folder):

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

myService.service('service1', function () {  
    this.return1 = function () {
        return 1;
    };
});

I placed the two following test-files in the spec folder inside the test folder in the project. To test the controller, I wrote the following in a file called controllerSpec.js:

describe('Person controller', function () {  
    beforeEach(module('personController'));

    describe('pController', function () {

        it('should create people array with 3 people', inject(function ($controller) {
            var scope = {};
            var ctrl = $controller('pController', { $scope: scope });

            expect(scope.people.length).toBe(3);
        }));
    });
});

To test the service, I wrote the following in a file called servicesSpec.js:

describe('service1', function () {  
    beforeEach(module('myService'));
    var service1 = {};

    beforeEach(inject(function (_service1_) {
        service1 = _service1_;

    }));

    describe('return 1', function() {
        it('should return 1', inject(function() {
            var one = service1.return1();

            expect(one).toBe(1);
        }));
    });
});

To run these simple tests and that everything is set up correctly, click the green arrow button in the top right corner in the WebStorm IDE.
If you get a message saying the tests passed, you are ready to write real tests.

Link to the example project:
https://github.com/ckirschberg/AngularUnitTestWithJasmine