Angular Test First Workflow

The context

Single page applications are significant part of a modern web. Their scale is increasing from simple tools to large webpages. Because of that, we as developers have to be sure that our apps are working as expected, and that new features we are implementing would not break other parts of our page.

According to W3Thechs, high traffic sites, such as, are using AngularJS library. The fast-growing popularity of AngularJS in comparison to ember.js and backbone.js is also illustrated in the graph bellow:

In this article we will discuss how to create simple but efficient workflow for writing unit tests with Grunt and AngularJS. The main tools to be used are:

  • NPM, Bower - package managers
  • Grunt – the JavaScript task runner
  • Karma – JavaScript test runner
  • Jasmine – JavaScript testing framework

The main goal of this tutorial is to run unit tests automatically during the development, and create a simple test-first workflow.

First of all, let us install few packages with NPM:

npm install grunt grunt-cli grunt-contrib-connect grunt-contrib-watch serve-static --save-dev

These are packages required for our local server. Another important group of packages are those, required for karma:

npm install grunt-karma jasmine-core karma karma-jasmine karma-phantomjs-launcher --save-dev

After installing packages in your package.json file you will find a section with devDependencies:

"devDependencies": {
	"grunt-cli": "^0.1.13",
	"grunt-contrib-connect": "^0.11.2",
	"grunt-contrib-watch": "^0.6.1",
	"grunt-karma": "^0.12.0",
	"jasmine-core": "^2.3.4",
	"karma": "^0.13.9",
	"karma-jasmine": "^0.3.6",
	"karma-phantomjs-launcher": "^0.2.1",
	"phantomjs": "^1.9.18",
	"serve-static": "^1.10.0"

Although we have some packages for Grunt installed, we still need the AngularJS. Now we will install it using Bower.

bower install angular --save-dev
bower install angular-mocks --save-dev

After installing bower componenets, we need to create a Gruntfile.js – a file with our tasks. If you are not sure how to do this, please have a look at the documentation

Now my file looks as follows:

'use strict';
var serveStatic = require('serve-static');
module.exports = function (grunt) {
        connect: {
            options: {
                port: 9000,
                hostname: 'localhost',
                livereload: 35729,
            livereload: {
                options: {
                    open: true,
                    base: [
                    middleware: function (connect, options, middlewares) {

                        return middlewares;
        watch: {
            js: {
                files: ['app/*.js', 'app/**/*.js'],
                // runs karma after saving any *.js file
                // in app folder (app.js or app_tests.js)
                tasks: ['karma:unit:run'],
                options: {
                    livereload: '<%= connect.options.livereload %>'
            livereload: {
                options: {
                    livereload: '<%= connect.options.livereload %>'
                files: [
        // grunt-karma configuration
        karma: {
            options: {
                // framework installed with package karma-jasmine
                frameworks: ['jasmine'],
                // runner installed with package karma-phantomjs-launcher
                browsers: ['PhantomJS'],
            unit: {
                // runs karma in a child process
                background: true,
                // keeps karma server running after the test execution
                singleRun: false,
                runnerPort: 9999,
                // files that will be available in PhantomJS
                files: [{
                    src: [

    // Requiered for grunt tasks

    grunt.registerTask('live', [
        // start karma server
        // start local server
        // listen to changes in app files

Please have a look on the section karma. There are two main properties which help us run tests in the background:

background: true,
singleRun: false

Also, we added karma task in watch section for each and every change of *.js files:

tasks: ['karma:unit:run']

The above written code is the karma trigger that runs the tests.

Last but not least, we need to keep both the karma and local servers running. In order to do this, please create a new task:

grunt.registerTask('live', [
    // start karma server
    // start local server
    // listen to changes in app files

In console, after entering the command grunt live, these tasks will be executed. The first one is karma:unit, which starts karma server, the second one – connect-livereload – starts local server with live reload option. The last one – watch – is listening to changes in the files.

As soon as we finish the configuration, we create a new app. What we need is app/index.html, app/scripts/app.js and app/scripts/app_tests.js. In this project they are in the app folder.

<!doctype html>
<html lang="en" ng-app="AngularUnitFlow">
    <title>Angular Unit test workflow</title>
    <div ng-controller="MainController"></div>
    <script src="/bower_components/angular/angular.js"></script>
    <script src="/scripts/app.js"></script>
angular.module('AngularUnitFlow', []);

.controller('MainController', ['$scope', function($scope){
	// place for new functionality
describe('App', function() {


    describe('Main Controller', function () {
        var scope, controller;
        beforeEach(inject(function ($controller, $rootScope) {
            scope = $rootScope.$new();
            controller = $controller('MainController', { $scope:scope });

        it('should be defined', inject(function($controller) {

        // in this place you need to add new tests

Folder structure

|- app/
    |- scripts/
        |- app.js
        |- app_tests.js
    |- index.html
|- bower.json
|- Gruntfile.js
|- package.json

Run command grunt live to run the server and add the first test:

it('should has string variable', function() {
    expect(scope.testableVariable).toEqual('This is a string');

After saving the file in the console you will find:

>> File "app\scripts\app_tests.js" changed.
Running "karma:unit:run" (karma) task
PhantomJS 1.9.8 (Windows 8 0.0.0) App Main Controller should has string variable FAILED
        Expected undefined to equal 'This is a string'.
            at app/scripts/app_tests.js:17
PhantomJS 1.9.8 (Windows 8 0.0.0): Executed 2 of 2 (1 FAILED) (0 secs / 0.009 secsPhantomJS 1.9.8 (Windows 8 0.0.0): Executed 2 of 2 (1 FAILED) (0.001 secs / 0.009 secs)
      Warning: Task "karma:unit:run" failed. Use --force to continue.

Aborted due to warnings.

The test we have written failed, but this is fine. Now, we will write code for our test. In app.js you need to add a new variable in MainController controller:

$scope.testableVariable = 'This is a string';

Now please save the file and have a look at the console window:

>> File "app\scripts\app.js" changed.
Running "karma:unit:run" (karma) task
PhantomJS 1.9.8 (Windows 8 0.0.0): Executed 2 of 2 SUCCESS (0 secs / 0.007 secs)

Done, without errors.

Source code

As you can see, now we have our project configured for test-first development. The whole project is available here. Please download the archive, unpack to any folder and install there the packages with commands: npm install and bower install. Now start the project with command grunt live and… have fun!