Performance of unit testing with AngularJS

Speaker: Dmytro Golysh

Software engineer

In our project we have optimized unit tests, and I am speaking about how

Questions at the end!!!

For whom the presentation can help

For AngularJS developers, who use to:

What can use:

Use stopwatch and system monitor

What can use:

Use chrome memory profiler to find leaks

Problems in unit tests with AngularJS

Test execution speed

Allocated memory

Which browser to choose?

Browsers for running tests:

Headless browsers for running tests:

JSDom

. . .

Speed:

Speed:

Problem with angular.mock.module

beforeEach(module('app'));

beforeEach(function() {
    module('app');
});
(function() {
    'use strict';

    angular
        .module('app')
        .run(setupPos);

    /*@ngInject*/
    function setupPos(PosSetupService) {
        PosSetupService.setup();
    }
})();
(function() {
    'use strict';

    angular
        .module('app')
        .config(routeConfig);

    /*@ngInject*/
    function routeConfig($stateProvider) {
        $stateProvider
            .state('store.previousTransaction'
..............................................

Speed:

Problem with angular.mock.inject

    beforeEach(function() {
        module('app');

        inject(function(PreviousTransactionService,
                        _$q_,
                        _whttp_,
                        _$timeout_,
                        _TransactionStore_) {

            sut = PreviousTransactionService;
            $q = _$q_;
            whttp = _whttp_;
            $timeout = _$timeout_;
            TransactionStore = _TransactionStore_;
        });

        data = {skus: {}};

        whttp.get = env.stub().returns($q.when({data: data}));
        TransactionStore.update = env.stub();
    });

Speed:

Problem with angular.mock.inject

Use $controller instead of inject when we are testing controller


        inject(function($controller,
                        _$q_,
                        _$timeout_) {
            $q = _$q_;
            $timeout = _$timeout_;

            sut = $controller('TransactionSuspendController', {
                $scope: $scope,
                SuspendedTransactionsService: SuspendedTransactionsService,
                TransactionManagerService: TransactionManagerService,
                DialogService: DialogService
            });
        });

Speed:

Problem with angular.mock.inject

Use import in ES6 or require

import PosRegisterController from './PosRegisterController';

describe('app::pos-register: PosRegisterController', () => {
    /*Define variables: let sut; ...*/

    beforeEach(() => {
        devicesList = {
            totalElements: 2,
            content: []
        };
        $state = {
            params: {},
            go: env.stub()
        };
        PosRegisterService = {};

        sut = new PosRegisterController(devicesList, $state, PosRegisterService);
    });

Memory:

Memory:

How to create giant memory leaks in AngularJS

app.directive('foo', function() {
    return {
        link: function(scope, element, attributes) {
            $document.on('click', function() {
              // code here
  • Register events on window, document or body

To prevent leaks you have to:

app.directive('foo', function() {
    return {
        link: function(scope, element, attributes) {            
            var listener = function() {/* code here */};
            $document.on('click', listener);
            return scope.$on('$destroy', function() {
                return $document.off('click', listener);
            });
        }
    };
});

Memory:

How to create giant memory leaks in AngularJS

app.directive('foo', function($rootScope) {
    return {
        link: function(scope, element, attributes) {
            return $rootScope.$on('event', function() {
                return scope.attribute = value;
            });
  • $rootScope listeners

To prevent leaks you have to:

app.directive('foo', function($rootScope) {
    return {
        link: function(scope, element, attributes) {
            var unsubscribe = $rootScope.$on('event', function() {
                return scope.key = value;
            });
            return scope.$on('$destroy', unsubscribe);
        }
..................................................................

Memory:

How to create giant memory leaks in AngularJS

app.directive('clock', function() {
    return {
        link: function(scope, element, attributes) {
            var updateTime = function() {
                return element.text((new Date()).toString());
            };
            return setInterval(updateTime, 1000);
  • Fail to clear intervals and timeouts

To prevent leaks you have to:

app.directive('clock', function() {
    return {
        link: function(scope, element, attributes) {
          var interval, updateTime;
          updateTime = function() {
            return element.text((new Date()).toString());
          };
          interval = setInterval(updateTime, 1000);
          return scope.$on('$destroy', function() {
            return clearInterval(interval);
          });

Memory:

How to create giant memory leaks in AngularJS

console.log(scope)
  • Output structured objects with console.log
var myScope = scope.$new() /* need remove by using myScope.$destroy(); */
  • Fail to clean up scopes you created yourself
var observer = function() { /* observe expression */ };

var unsubscribe = $rootScope.$watch(observer, function(newValue, oldValue) {
    /* code to run if the expression changes */
});

/* later, when you no longer need the watch */
unsubscribe();
  • Fail to clean up watches on $rootScope

Conclusions. Advices

  • Try to exclude config and run blocks
  • Use import in ES6 instead of inject
  • Avoid memory leaks in AngularJS
  • karma-phantomjs-launcher

Bonus:

v1.0.0 use PhantomJS 2.1.1 released 20 days ago

Questions

Performance of unit testing Angular JS

By Dmytro Golysh

Performance of unit testing Angular JS

  • 638