SYSTEM UNDER TESTING

TESTING VARIETY

TESTING TYPES

Black box

Performance

Load testing

Usability testing

End-to-end

White box

Functional

Compatibility

Stress testing

Recovery

Unit testing

Integration

Security testing

Regression

Acceptance

UNIT TESTING PLACE

Unit tests

Integration tests

Acceptance tests

UI tests

TESTING GLOSSARY

FAKE

SPY

STUB

MOCK

TESTING SERVER

WHY START WITH SERVER

Common place to test

Mostly logic

Easier to automate

CHALLENGES ON SERVER

Routing

Database interactions

Knowing what  to test

HOW USERS SEE API

Route

Middleware

Business objects

HOW WE TEST API

Middleware

Business objects

Route

SUMMARY

Test only app functionality

Server testing is straightforward

Don't forget to refactor

TESTING CLIENT

CHALLENGES ON CLIENT

User interaction

Framework "Magic"

Appearance

HOW USERS SEE APP

User interface

Data for user interface

Source of data

HOW WE TEST APP

User interface

Data for user interface

Source of data

SUMMARY

Client testing needs change of perspective

Fake out all levels below the current one

Don’t Forget to Refactor

TESTING TOOLS

MOCHA

CHAI

SINON

Sinon.JS

JASMINE

KARMA

 JEST

TESTING PRACTICE

DEFINING A MODULE

describe( 'Notes:', function() {

    var Notes = window.modules.Notes;

    it( 'should Notes module to be defined:', function() {
        expect( Notes ).toBeDefined();
    } );

} );

DEFINING A MODULE

( function( window ) {

    function Notes() {}

    window.modules = window.modules || {};
    window.modules.Notes = Notes;

} )( window );

INITIALIZING A MODULE

describe( 'Notes:', function() {
    var Notes = window.modules.Notes;

    it( 'should Notes module to be defined:', function() {
        expect( Notes ).toBeDefined();
    } );

    describe( 'initialization=>', function() {

        beforeEach( function() {
            env.stub( Notes.prototype, 'bindEvents' );
            this.sut = new Notes();
        } );
    
        it( 'should bind events:', function() {
            expect( this.sut.bindEvents ).toHaveBeenCalled();
        } );
    
    } );

} );

INITIALIZING A MODULE

( function( window, $ ) {

    function Notes() {
        this.init();
    }

    $.extend( Notes.prototype, {

        init: function() {
            this.bindEvents();
        },
        
        bindEvents: function() {}

    } );

    window.modules = window.modules || {};
    window.modules.Notes = Notes;

} )( window, jQuery );

RESOLVING PROMISES

describe( 'initialization=>', function() {

    beforeEach( function() {
        this.fakeDeferred = new $.Deferred();
        
        env.stub( Notes.prototype, 'getNotes' );
        env.stub( Notes.prototype, 'bindEvents' );
        
        Object.defineProperty( window, 'authReady', {
            value: this.fakeDeferred
        } );
        
        this.sut = new Notes();
    } );

    describe( 'user is NOT signed in=>', function() {

        beforeEach( function() {
            this.fakeDeferred.resolve( { user: { isLoggedIn: false } } );
        } );

        it( 'should NOT bind events:', function() {
            expect( this.sut.bindEvents ).not.toHaveBeenCalled();
        } );

        it( 'should NOT get notes:', function() {
            expect( this.sut.getNotes ).not.toHaveBeenCalled();
        } );

    } );

    describe( 'user is signed in=>', function() {

        beforeEach( function() {
            this.fakeDeferred.resolve( { user: { isLoggedIn: true } } );
        } );

        it( 'should bind events:', function() {
            expect( this.sut.bindEvents ).toHaveBeenCalled();
        } );

        it( 'should NOT get notes:', function() {
            expect( this.sut.getNotes ).toHaveBeenCalled();
        } );

    } );

} );

RESOLVING PROMISES

( function( window, $ ) {

    function Notes() {
        this.init();
    }

    $.extend( Notes.prototype, {

        init: function() {

            window.authReady.then( function( auth ) {

                if ( !auth.user.isLoggedIn ) {
                    return;
                }

                this.getNotes();
                this.bindEvents();

            }.bind( this ) );

        },
        
        bindEvents: function() {},

        getNotes: function() {}

    } );

    window.modules = window.modules || {};
    window.modules.Notes = Notes;

} )( window, jQuery );

BINDING EVENTS

describe( 'binding events=>', function() {

    beforeEach( function() {
        env.stub( Notes.prototype, 'init' );
        
        this.sut = new Notes();
        this.sut.$form = {
            on: env.stub()
        };
    } );

    it( 'should bind submit handler on save button:', function() {
        this.sut.bindEvents();
        expect( this.sut.$form.on ).toHaveBeenCalledWith( 'submit', this.sut.onSubmit );
    } );

} );

BINDING EVENTS

( function( window, $ ) {

    function Notes() {
        this.$form = $( '.js-RC-notes' )
        this.init();
    }

    $.extend( Notes.prototype, {

        init: function() {

            window.authReady.then( function( auth ) {

                if ( !auth.user.isLoggedIn ) {
                    return;
                }

                this.getNotes();
                this.bindEvents();

            }.bind( this ) );

        },
        
        bindEvents: function() {
            this.$form.on( 'submit', $.proxy( this.onSubmit, this ) );
        },

        getNotes: function() {},

        onSubmit: function( event ) {}

    } );

    window.modules = window.modules || {};
    window.modules.Notes = Notes;

} )( window, jQuery );

AJAXING DATA

describe( 'getting notes=>', function() {

    beforeEach( function() {
        this.fakeParams = {
            'race_id': '123',
            'horse_id': [ 12, 23 ],
            'race_type_code': 'F'
        };
        this.fakeConfig = {
            type: 'GET',
            url: '/racecards/ugc',
            data: this.fakeParams
        };
        this.fakeDeferred = new $.Deferred();

        env.stub( Notes.prototype, 'init' );
        env.stub( Notes.prototype, 'processParams' ).returns( this.fakeParams );
        env.stub( $, 'ajax' ).withArgs( this.fakeConfig  ).returns( this.fakeDeferred );

        this.sut = new Notes();
    } );

    describe( 'request is successful=>', function() {
        beforeEach( function() {
            this.fakeSuccessData = {
                success: true,
                error: 'error message',
                data: { 123: {
                    comment: 'comment',
                    rating: 'rating',
                    odds: 'odds',
                    selection: true
                } }
            };

            env.stub( Notes.prototype, 'getSuccess' );
        } );

        it( 'should render notes:', function() {
            this.sut.getNotes();
            this.fakeDeferred.resolve( this.fakeSuccessData );
            
            expect( this.sut.getSuccess ).toHaveBeenCalledWith( this.fakeParams, this.fakeSuccessData );
        } );

    } );

    describe( 'request is failed=>', function() {
        beforeEach( function() {
            this.fakeErrorData = {
                message: 'error message'
            };

            env.stub( Notes.prototype, 'getError' );
        } );

        it( 'should handle error:', function() {
            this.sut.getNotes();
            this.fakeDeferred.reject( this.fakeErrorData );
            
            expect( this.sut.getError ).toHaveBeenCalledWith( this.fakeErrorData );
        } );

    } );

} );

AJAXING DATA

( function( window, $ ) {

    function Notes() {
        this.$form = $( '.js-RC-notes' )
        this.init();
    }

    $.extend( Notes.prototype, {

        init: function() {

            window.authReady.then( function( auth ) {

                if ( !auth.user.isLoggedIn ) {
                    return;
                }

                this.getNotes();
                this.bindEvents();

            }.bind( this ) );

        },
        
        bindEvents: function() {
            this.$form.on( 'submit', $.proxy( this.onSubmit, this ) );
        },

         getNotes: function() {
            var requestParams = this.processParams();

            $.ajax( {
                type: 'GET', 
                url: '/racecards/ugc', 
                data: requestParams
            } )
             .done( $.proxy( this.getSuccess, this, requestParams ) )
             .fail( $.proxy( this.getError, this ) );

        },

        onSubmit: function( event ) {},

        processParams: function() {},

        getSuccess: function( requestParams, response ) {},

        getError: function( error ) {}

    } );

    window.modules = window.modules || {};
    window.modules.Notes = Notes;

} )( window, jQuery );

QUESTIONS UNDER TESTING

System Under Testing

By Roman Stremedlovskyi

System Under Testing

Unit Testing in JavaScript projects

  • 1,486