Unit testing
Overview • Unit testing frameworks • Jasmine (in details) • Test runners • Karma (in details) • Angular mocks • Testing controllers • Testing services
Unit testing frameworks • Jasmine – Exposes BDD no additional set up and configuration required • Mocha – Requires additional configuration can work as TDD or BDD. Normally is being combined with Chai • QUnit
Jasmine • Requires less setup • Is integrated with most CI • Is supported by test runners or can run from browser • Is fully BDD • Has built in assertion library • Does not have built in mocking frameworks(works fine with 3rd party like Squire or angular-mocks) • The syntax is really good
Mocha • Requires more setup • Is not fully integrated CI • Although can be executed via test runners or CI plugins • Provides ability to choose between TDD or BDD • Works fine with 3rd party assertion and mocking libraries • Is integrated with IDE(Web Storm) • Testing async is smooth
Jasmineshortest testsample //describe function for the test suite describe("This is test suite", function() { //it function for the spec it("contains spec with an expectation", function() { //expect assertion function expect(true) //toBe is a matcher function .toBe(true); }); });
Jasminetest suite • Is done with describe function • describe expect as a first argument string with test suite description as a second function to be called to process test suite • Can be nested Test suite is a collection of test cases that are intended to be used to test a software program to show that it has some specified set of behaviors. A test suite often contains detailed instructions or goals for each collection of test cases and information on the system configuration to be used during testing. Wiki (c)
Jasminetest spec • Is done with it function • it expect as a first argument string with test suite description as a second function to be called to process test suite • Contain expectations
Jasmineexpectations • Expectations are built with the function expect which takes a value, called the actual. It is chained with a Matcher function, which takes the expected value. expect(true).toBe(true); expect(true).not.toBe(false);
Matcherfunctions • toBe() • toEqual() • toMatch() • toBeDefined() • toContain() • toBeGreaterThan() • toBeNull() • And others • You can create custom matchers as well • Useful library jasmine-matchers
JasmineTearUpandTearDown • beforeEach – tear up • afterEach – tear down describe("A spec (with setup and tear-down)", function() { var foo = 0; //will add 1 before each spec beforeEach(function() { foo += 1; }); //will set it to 0 after each spec afterEach(function() { foo = 0; }); //2 specs will verify that foo is equal to 1 it("is just a function, so it can contain any code", function() { expect(foo).toEqual(1); }); it("can have more than one expectation", function() { expect(foo).toEqual(1); }); });
Jasminespies • At the very high level can be treated as mocks • Can mock the call and record passed parameters • Can return fake values • Can mock the call and pass through to real function spyOn(foo, "getBar").and.returnValue(745); spyOn(foo, 'getBar').and.callThrough(); spyOn(foo, 'setBar');
Jasminespies describe("Spy sample", function() { var foo, bar = null; beforeEach(function() { foo = { setBar: function(value) { bar = value; } }; //spy on set bar method spyOn(foo, 'setBar'); foo.setBar(123); foo.setBar(456); }); it("tracks that the spy was called", function() { expect(foo.setBar).toHaveBeenCalled(); }); it("tracks all the arguments of its calls", function() { expect(foo.setBar).toHaveBeenCalledWith(123); expect(foo.setBar).toHaveBeenCalledWith(456); }); it("stops all execution on a function", function() { expect(bar).toBeNull(); }); });
So how to run unit tests? • Browser • Test runner – Karma – Testem
Karma • Runs tests in background in different browsers • Once code is changed on file system reruns tests automatically • Runs from the console • Is integrated with IDE (WebStorm, VS) • Requires NodeJS to be installed
Karmaconfiguration • Install nodejs • npm install -g karma • npm install -g karma-cli • Additional packs: – karma-jasmine – karma-junit-reporter – karma-ng-scenario – e2e testing – karma-ng-html2js-preprocessor – load all templates and do not load them from server – karma-phantomjs-launcher
Karmaconfiguration Sample: http://jsfiddle.net/gdn0jocf/ • Doc: http://karma- runner.github.io/0.8/config/configuratio n-file.html
How to run it? • Command line: karma start configfilename • Grunt task grunt task DEMO
Unit testcontrollersample describe('Home controller test', function () { //loading module where controller is defined beforeEach(module('app.home')); //declaring variables that will be used in the tests var controller, scope, deferred; //creating items beforeEach(inject(function ($rootScope, $controller, $q) { deferred = $q.defer(); scope = $rootScope.$new(); //create the controller injecting the scope and the mocked service controller = $controller('Home', { $scope: scope, DashboardService: { getDashboard: function () { return deferred.promise; } } }); })); //once result is not returned let's check that initial data state is correct it('verifies NewMessagesCount is undefined', function () { expect(controller.NewMessagesCount === undefined); }); //Let's resolve value and see if it is correct it('verifies NewMessagesCount is correct', function () { deferred.resolve({ NewMessagesCount: 5 }); expect(controller.NewMessagesCount === 5); }); it('verifies that scope contains go and it is a function', function () { expect(scope.go === 'function'); }); });
Unit testservicesample describe('Dashboard factory tests', function () { //injecting module beforeEach(module('app.services')); //mocking dependcies beforeEach(function () { var Utility = {}; module(function ($provide) { $provide.value('Utility', Utility); }); }); var httpBackend, Factory; //injecting httpBackend for testing of http //injecting factory itself beforeEach(inject(function ($httpBackend, Factory) { httpBackend = $httpBackend; Factory = Factory; })); it('checks that object is not modified by service and proper API method is called', function () { //setting method we expect to be called and method response httpBackend.expectGET('api/Dashboard/').respond("Test"); var result = Factory.getDashboard(); //Verifying that all expected methods were called httpBackend.flush(); //verifying that result is returned as expected expect(result == "Test"); }); afterEach(function () { httpBackend.verifyNoOutstandingExpectation(); httpBackend.verifyNoOutstandingRequest(); }); });
Unit teste2e sample describe( ‘Angular App', function () { describe(‘Angular libraries list', function () { beforeEach(function () { browser().navigateTo('/app/index.html'); }); it( 'should filter the list as user types into the search box', function () { expect(repeater('.users li').count()).toBe(5); input('query').enter(‘angular-ui'); expect(repeater('.users li').count()).toBe(1); input('query').enter(''); expect(repeater('.users li').count()).toBe(5); } ); } );});
Unit testing in JavaScript with Jasmine and Karma

Unit testing in JavaScript with Jasmine and Karma

  • 1.
  • 2.
    Overview • Unit testingframeworks • Jasmine (in details) • Test runners • Karma (in details) • Angular mocks • Testing controllers • Testing services
  • 3.
    Unit testing frameworks •Jasmine – Exposes BDD no additional set up and configuration required • Mocha – Requires additional configuration can work as TDD or BDD. Normally is being combined with Chai • QUnit
  • 4.
    Jasmine • Requires lesssetup • Is integrated with most CI • Is supported by test runners or can run from browser • Is fully BDD • Has built in assertion library • Does not have built in mocking frameworks(works fine with 3rd party like Squire or angular-mocks) • The syntax is really good
  • 5.
    Mocha • Requires moresetup • Is not fully integrated CI • Although can be executed via test runners or CI plugins • Provides ability to choose between TDD or BDD • Works fine with 3rd party assertion and mocking libraries • Is integrated with IDE(Web Storm) • Testing async is smooth
  • 6.
    Jasmineshortest testsample //describe functionfor the test suite describe("This is test suite", function() { //it function for the spec it("contains spec with an expectation", function() { //expect assertion function expect(true) //toBe is a matcher function .toBe(true); }); });
  • 7.
    Jasminetest suite • Isdone with describe function • describe expect as a first argument string with test suite description as a second function to be called to process test suite • Can be nested Test suite is a collection of test cases that are intended to be used to test a software program to show that it has some specified set of behaviors. A test suite often contains detailed instructions or goals for each collection of test cases and information on the system configuration to be used during testing. Wiki (c)
  • 8.
    Jasminetest spec • Isdone with it function • it expect as a first argument string with test suite description as a second function to be called to process test suite • Contain expectations
  • 9.
    Jasmineexpectations • Expectations arebuilt with the function expect which takes a value, called the actual. It is chained with a Matcher function, which takes the expected value. expect(true).toBe(true); expect(true).not.toBe(false);
  • 10.
    Matcherfunctions • toBe() • toEqual() •toMatch() • toBeDefined() • toContain() • toBeGreaterThan() • toBeNull() • And others • You can create custom matchers as well • Useful library jasmine-matchers
  • 11.
    JasmineTearUpandTearDown • beforeEach –tear up • afterEach – tear down describe("A spec (with setup and tear-down)", function() { var foo = 0; //will add 1 before each spec beforeEach(function() { foo += 1; }); //will set it to 0 after each spec afterEach(function() { foo = 0; }); //2 specs will verify that foo is equal to 1 it("is just a function, so it can contain any code", function() { expect(foo).toEqual(1); }); it("can have more than one expectation", function() { expect(foo).toEqual(1); }); });
  • 12.
    Jasminespies • At thevery high level can be treated as mocks • Can mock the call and record passed parameters • Can return fake values • Can mock the call and pass through to real function spyOn(foo, "getBar").and.returnValue(745); spyOn(foo, 'getBar').and.callThrough(); spyOn(foo, 'setBar');
  • 13.
    Jasminespies describe("Spy sample", function(){ var foo, bar = null; beforeEach(function() { foo = { setBar: function(value) { bar = value; } }; //spy on set bar method spyOn(foo, 'setBar'); foo.setBar(123); foo.setBar(456); }); it("tracks that the spy was called", function() { expect(foo.setBar).toHaveBeenCalled(); }); it("tracks all the arguments of its calls", function() { expect(foo.setBar).toHaveBeenCalledWith(123); expect(foo.setBar).toHaveBeenCalledWith(456); }); it("stops all execution on a function", function() { expect(bar).toBeNull(); }); });
  • 14.
    So how torun unit tests? • Browser • Test runner – Karma – Testem
  • 15.
    Karma • Runs testsin background in different browsers • Once code is changed on file system reruns tests automatically • Runs from the console • Is integrated with IDE (WebStorm, VS) • Requires NodeJS to be installed
  • 16.
    Karmaconfiguration • Install nodejs •npm install -g karma • npm install -g karma-cli • Additional packs: – karma-jasmine – karma-junit-reporter – karma-ng-scenario – e2e testing – karma-ng-html2js-preprocessor – load all templates and do not load them from server – karma-phantomjs-launcher
  • 17.
    Karmaconfiguration Sample: http://jsfiddle.net/gdn0jocf/ • Doc:http://karma- runner.github.io/0.8/config/configuratio n-file.html
  • 18.
    How to runit? • Command line: karma start configfilename • Grunt task grunt task DEMO
  • 19.
    Unit testcontrollersample describe('Home controllertest', function () { //loading module where controller is defined beforeEach(module('app.home')); //declaring variables that will be used in the tests var controller, scope, deferred; //creating items beforeEach(inject(function ($rootScope, $controller, $q) { deferred = $q.defer(); scope = $rootScope.$new(); //create the controller injecting the scope and the mocked service controller = $controller('Home', { $scope: scope, DashboardService: { getDashboard: function () { return deferred.promise; } } }); })); //once result is not returned let's check that initial data state is correct it('verifies NewMessagesCount is undefined', function () { expect(controller.NewMessagesCount === undefined); }); //Let's resolve value and see if it is correct it('verifies NewMessagesCount is correct', function () { deferred.resolve({ NewMessagesCount: 5 }); expect(controller.NewMessagesCount === 5); }); it('verifies that scope contains go and it is a function', function () { expect(scope.go === 'function'); }); });
  • 20.
    Unit testservicesample describe('Dashboard factorytests', function () { //injecting module beforeEach(module('app.services')); //mocking dependcies beforeEach(function () { var Utility = {}; module(function ($provide) { $provide.value('Utility', Utility); }); }); var httpBackend, Factory; //injecting httpBackend for testing of http //injecting factory itself beforeEach(inject(function ($httpBackend, Factory) { httpBackend = $httpBackend; Factory = Factory; })); it('checks that object is not modified by service and proper API method is called', function () { //setting method we expect to be called and method response httpBackend.expectGET('api/Dashboard/').respond("Test"); var result = Factory.getDashboard(); //Verifying that all expected methods were called httpBackend.flush(); //verifying that result is returned as expected expect(result == "Test"); }); afterEach(function () { httpBackend.verifyNoOutstandingExpectation(); httpBackend.verifyNoOutstandingRequest(); }); });
  • 21.
    Unit teste2e sample describe( ‘AngularApp', function () { describe(‘Angular libraries list', function () { beforeEach(function () { browser().navigateTo('/app/index.html'); }); it( 'should filter the list as user types into the search box', function () { expect(repeater('.users li').count()).toBe(5); input('query').enter(‘angular-ui'); expect(repeater('.users li').count()).toBe(1); input('query').enter(''); expect(repeater('.users li').count()).toBe(5); } ); } );});