Subscribe to receive all latest blog updates

JavaScript testing with Jasmine presents an efficient and convenient way to cover your code with test early in development, allowing to greatly improve it in most cases. In this article, I will explore Behavior Driver Development (BDD) paradigm and show the basics of JavaScript testing with this framework.

There are some theoretical information and practical examples. I will also give you an instruction on how to set up your Visual Studio to run tests in this framework the same way as common .NET tests. This Jasmine testing tutorial will be useful for both beginners and developers who want to use it for their .NET projects.

You can always learn more about our .NET development expertise.

Written by:

Nikita Evdokimenko,
Junior .NET Developer

  1. Introduction
  2. Jasmine review
  3. Set up environment
  4. How to test with Jasmine
  5. Using Jasmine with AngularJS
  6. Summary

1. Introduction

Imagine writing .NET project with JavaScript. JQuery is the best JavaScript library for HTML client-side scripting, but even if you know what you’re doing with it, it’s a good idea to practice caution. Your implementation of JQuery may appear to work fine. However, it is almost guaranteed that the code has some hidden problems and the only way to uncover them is to start testing.

Testing your code during development is one of the best practices you can follow. However, testing on a high quality level may be quite hard: you need to retest your functionality after implementing changes to prevent regression, build and manually test features while developing. It takes a great deal of developer’s time and even then, some of these important issues might still left unresolved. Finally when you have something broken you cannot say what exactly is wrong at once. If this situation is familiar to you, then covering your code with tests is the solution. A great way to test your code is to use Jasmine testing tool.

So, writing tests for your project will:

  • Prevent regression. You can run your test before committing your changes to make sure the old functionality works fine.
  • Reduce manual testing. There are always operations that can be tested without manual testing (result of search, business calculations, data formatting)
  • Check yourself. When you write one more test, you may find a case that will surely break your code logic before running tests.
  • Make your code cleaner. To write tests easily, your code needs to be well separated, contain Inversion of Control (so you can mock it) and have a possibility to be configured. It is really terrible to cover hardcoded functionality.
  • Satisfy business requirements. Sometimes customers require a certain percentage of test coverage.

In this article I will show your how to cover your JavaScript logic with Jasmine unit testing.

2. Jasmine review

Jasmine is a behavior-driven development framework for testing JavaScript code. It is free, has a simple syntax and provides a nice set of features. Furthermore, community surrounding this framework is large, so it is easy to find solutions for the problems you may face. Unit testing with this framework provides a fast and simple way to cover your codes with tests.

There is a great way to describe what BDD stands for, and also describe the syntax of Jasmine: “In a BDD style test you describe your code and tell the test what it should be doing. Then you expect your code to do something.”

A simple example of Jasmine JavaScript testing looks like this:

describe("math", function () {
        it("should sum 1+1", function () {
            expect(1 + 1).toEqual(2);
        });
    });
		

Think about “describe” as an equivalent of “namespace” that helps you organize your code. “It” is your unit test. I will explain this in more detail later, after I show you how to set up your environment for Jasmine.

3. Set up the environment

You can download Jasmine at https://github.com/jasmine/jasmine/releases. Unzip it an you will see:

  • Lib: contains source code for the framework
  • Spec: contains code for your tests
  • Src: contains source code for your application
  • SpecRunner.html is a page that runs your tests. You can see references to your specs (tests) and source code for the application you want to test. So it is not necessary to copy your code to “src” folder, you can just link your specs in this file.

This package also contains a little example that might be useful.

If you are using Visual Studio, just go to “Tools -> NuGet Package Manager -> Package Manager Console”. Type “Install-Package JasmineTest”.

Image shows an example of how to install Jasmine JavaScript testing package in Visual Studio. You can launch a Package Manager Console from the Tools menu and type “Install-Package JasmineTest”, to install Jasmine JS testing framework.

Jasmine engine, examples, and SpecRunner will be added to your project. If you are using MVC, you can create a Controller to run your test.

public class JasmineController : Controller
    {
        public ViewResult Run()
        {
            return View("SpecRunner");
        }
    }

Also there is possibility to run Jasmine test as a common unit test in Visual Studio. Go to “Tools -> Extentions and Updates”, click “Online” and search for “Chutzpah”. Install “Chutzpah Test Adapter for the Test Explorer”.

Image shows Extensions and Updates window with Jasmine unit test tool along with AngularJS and Chutzpah test adapter and test runner. JavaScript testing with Jasmine is a great way to cover your code with tests early in development.

Restart your Visual Studio and build the project. If you succeed, you will see all tests in Test Explorer.

Image shows Visual Studio Test Explorer with all the tests written via Jasmine unit testing tool.

Note! If you want to use Test Explorer to test your JavaScript, you need to add references to the source code only in your specs, as follows:

/// 
describe("App", function () {
    describe("foo", function () {
        it("should return bar", function () {
            expect(App.foo()).toEqual("bar");
        });
    });
});

So we link our spec to search “App” in “application.js” that is one level up. “application.js” contains the following code:

var App = {
    foo: function() {
        return "bar123";
    }
};

4. How to test with Jasmine JS testing

In the previous example we tested App.foo() using toEqual. There is a lot of other matchers:

  • .not.toEqual(expectedResult)
  • .toBeDefined()
  • .toBeUndefined()
  • .toBeNull()
  • .toBeTruthy()
  • .toBeFalsy()
  • .toBeLessThan(number)
  • .toBeGreaterThan(number)
  • .toContains(substring)
  • .toBeCloseTo(number, accuracy) – this one rounds up an argument in expect(argument) to the mentioned accuracy and compares it with a number.
  • .toMatch(/regexp/)
  • .toThrow() – catches exceptions and the test is passed if an exception was thrown. As an argument you can pass an error message you expect.

You can combine different matchers to write them one under another. The test will fail if any of them fail.

If you want to add your own matcher you can do it. Just add a new function to the spec you describing.

describe("foo", function () {
        beforeEach(function () {
            var matchers = {
                toBeHaveFirstLetter: function () {
                    return {
                        compare: function (actual, expected) {
                            return { pass: actual[0] === expected };
                        }
                    }
                }
            }
            jasmine.addMatchers(matchers);
        });
        it("should return bar", function () {
            expect(App.foo()).toEqual("bar");
        });
        it('first letter b', function () {
            expect(App.foo()).toBeHaveFirstLetter("b");
        });
    });

beforeEach is called before running every spec. There is also afterEach.

Same as every other testing framework, Jasmine has tools to mock functions and objects. It is called “spy”. Let’s add to our App object a new function doubleFoo that calls foo twice.

var App = {
    foo: function() {
        return "bar";
    },
    doubleFoo: function(){
        return this.foo() + " " + this.foo();
    }
};

And we create two test cases that use “spy”.

describe("foo", function () {
        it("called", function () {
            spyOn(App, 'foo');
            App.foo();
            expect(App.foo).toHaveBeenCalled();
        });
        it("called 2 time", function () {
            spyOn(App, 'foo');
            App.doubleFoo();
            expect(App.foo.calls.count()).toEqual(2);
        });
    });

The function spyOn mocks the function in the mentioned object, so we can learn how many times it was called and with which arguments. As you see, creating spy on App.foo allows us to read the property calls and get its count or inspect every call.

We can also get:

  • .toHaveBeenCalledWith(param)
  • .andCallThrough() – call the original function, not mock.

While creating spy we can change the return value or even the body of the function:

  • spyOn(object, “function”).andRetuns(“res”)
  • spyOn(object, “function”).andCallFake(function(){ return “bbar”})
  • jasmine.createSpyObj(‘obj', [ ‘func1', ‘func2']) – it will create an object, where all functions are mocked.

You can also Jasmine mock window or other global object by using dependency injections.

5. Using Jasmine with AngularJS

In this section, I will show how to easily integrate Jasmine JS testing with AngularJS framework. First of all, you need to add references to “angular.js”, “angular-mocks.js” and your source code with your angular entities. Then you need to create an instance of angular.module and controller you want to test. Let’s review an example. We have this simple angular application:

var myApp = angular.module('myApp', []);
myApp.controller('myController', function ($scope) {
    $scope.text = "qwe"
    $scope.caps = function()
    {
        $scope.text = "QWE"
    }
});

And here is our spec.

/// <reference path="../angular.js">
/// <reference path="../angular-mocks.js">
/// <reference path="../app/main.js">
describe('myApp', function () {
    var scope, controller;
    beforeEach(function () {
        module('myApp');
    });
    describe('myController', function () {
        beforeEach(inject(function ($rootScope, $controller) {
            scope = $rootScope.$new();
            controller = $controller('myController', {
                '$scope': scope
            });
        }));
        it('have default text', function () {
            expect(scope.text).toBe('qwe');
        });
        it('makes text to caps', function () {
            scope.caps();
            expect(scope.text).toBe('QWE');
        });
    });
});

We create an instance of angular.module and variables to store the scope and controller before testing every controller. Inside the test of every controller we create the new scope and the controller we want. Use inject to inject Angular services to beforeEach function. Then we pass our scope to the controller. After that we can test our Angular controller as described before.

I would also like to show you how we can mock and test requests to server with Angular. We have a controller which calls our API to get users by some group id.

myApp.controller('userController', function ($scope, $http) {
    $scope.getUsers = function (id) {
        return $http.get("/api/users/" + id).then(function (response) {
            if (response.data.success) {
                return response.data.data;
            }
            else {
                return null;
            }
        })
    };
})

Here is the description of the spec. I will explain it further.

describe('userController', function () {
        var $httpBackend,
            expectedUrl = '/api/users/123',
            promise;
        beforeEach(inject(function ($rootScope, $controller, _$httpBackend_) {
            $httpBackend = _$httpBackend_;
            scope = $rootScope.$new();
            controller = $controller('userController', {
                '$scope': scope
            });
        }));
        afterEach(function () {
            $httpBackend.verifyNoOutstandingExpectation();
            $httpBackend.verifyNoOutstandingRequest();
        });

We create some variables to store the promise and $httpBackend. Pay attention, that the scope and controller are described in myApp description. $httpBackend is a service to mock http requests. You can read more about it here: https://docs.angularjs.org/api/ngMock/service/$httpBackend. But now all you need to know is that it just allows us to mock requests. After each test we call $httpBackend.verifyNoOutstandingExpectation() and $httpBackend.verifyNoOutstandingRequest() to refresh $httpBackend.

it('returns http requests successfully', function () {
            var data = '{"success":"true", "data":"users"}';
            $httpBackend.expectGET(expectedUrl).respond(200, data);
            promise = scope.getUsers(123);
            promise.then(function (res) {
                expect(res).toBe('users');
            });
            $httpBackend.flush();
        });
        it('returns http requests with an error', function () {
            $httpBackend.expectGET(expectedUrl).respond(500, 'Oh no!!');
            promise = scope.getUsers(123);
            promise.then(function (res) {
                expect(res).toBeNull();
            });
            $httpBackend.flush();
        });

We mock request to expectedUrl (“/api/users/123”) to answer with success and return data. Our request returns the promise, so we wait until it finishes and check our result. Next test checks if our application returns null if we get an error from the server. Note, that here we don’t test requests sending and getting data, here we perform unit test to check only our JavaScript code and how it responds to the data returned from the server.

6. Summary

As you can see, Jasmine unit test is a great example on how to cover your JavaScript logic with tests using BDD. As it is integrated with AngularJS it also can be used with other frameworks. So I find it to be a really great testing tool. I hope you found this Jasmine JS tutorial useful, and will incorporate methods, described here into your own workflow.

References

Check official documentation for more information https://github.com/jasmine/jasmine/wiki.

Angular documentation is full of examples how to test it with Jasmine https://docs.angularjs.org/api.

If you want to learn more about BDD see http://dannorth.net/introducing-bdd/.

Articles I was referring to:

http://bittersweetryan.github.io/jasmine-presentation/

http://inviqa.com/blog/2014/10/28/testing-javascript-get-started-with-jasmine

http://code.tutsplus.com/tutorials/testing-your-javascript-with-jasmine--net-21229

 

Read also: Unity IoC Tutorial