Unit testing in JavaScript with Jasmine and Karma

3,424 views 22 slides Apr 03, 2015
Slide 1
Slide 1 of 22
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22

About This Presentation

Unit testing in JavaScript with Jasmine and Karma


Slide Content

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

Jasmine shortest test sample //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 ) ; }) ; }) ;

Jasmine test 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)

Jasmine test 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

Jasmine expectations 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 ) ;

Matcher functions toBe () toEqual () toMatch () toBeDefined () toContain () toBeGreaterThan () toBeNull () And others You can create custom matchers as well Useful library jasmine-matchers

Jasmine TearUp and TearDown beforeEach – tear up afterEach – tear down describe( "A spec (with setup and tear-down)" , function () { var foo = ; //will add 1 before each spec beforeEach ( function () { foo += 1 ; }) ; //will set it to 0 after each spec afterEach ( function () { foo = ; }) ; //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 ) ; }) ; }) ;

Jasmine spies 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 ' ) ;

Jasmine spies 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

Karma configuration 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

Karma configuration Sample: http ://jsfiddle.net/gdn0jocf/ Doc: http ://karma-runner.github.io/0.8/config/configuration-file.html

How to run it? Command line: karma start configfilename Grunt task grunt task DEMO

Unit test controller sample 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 test service sample 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 test e2e 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 ) ; } ) ; } ) ; }) ;