Mocking Service dependency in angularjs tests

Abhishek Jain

I have a service CurrentUser that has a dependency on StorageFactory. In the constructor of this service, if StorageFactory.get returns a user, I am setting the user to that user, otherwise setting a default user value. Now, I want to test this.

I have managed to make this work, but I am not happy with the approach I am using. I got the inspiration for this approach here.

I have pasted the code below. If you prefer, I have also created a gist here. I have removed the irrelevant part of the code to avoid distraction. This is written using ES6 classes and modules, but that shouldn't make any difference to the tests.

The problem with the approach is that the mock will be used across all the tests, which may not be a bad thing but I want to control that. Is there a way to make this mock take affect only for this test? One roadblock in finding better approach is that the mock has to be done before angular created the mock CurrentUserModule module. Is there a better way of testing this? I would appreciate any suggestions on this.

Service

import StorageFactoryModule from 'app/common/services/StorageFactory';

class CurrentUser {
    constructor(StorageFactory) {

    this.storageKey = 'appUser';

    this.StorageFactory = StorageFactory;

    this.profile = initializeUser.call(this);

    function initializeUser() {
        var userFromStorage = StorageFactory.get(this.storageKey);
        if (userFromStorage != null) {
            return userFromStorage;
        } else {
            // return default user
        }
    }

}
// more methods, that are removed for brevity

}

CurrentUser.$inject = ['StorageFactory'];

export default angular.module('CurrentUserModule', [StorageFactoryModule.name])
    .service('CurrentUser', CurrentUser);

Test

import angular from 'angular';
import 'angular-mocks';
import './CurrentUser';

describe('CurrentUser', function () {
    "use strict";
    var user = {userid: 'abc', token: 'token'};

    var storageFactoryMock = {
        get: function (key) {
            return user;
        },
        put: function (key, newUser) {
            user = newUser;
        },
        remove: function (key) {
            user = undefined;
        }
    };

    beforeEach(function () {
        angular.module('StorageFactoryModule')
            .value('StorageFactory', storageFactoryMock);
        angular.mock.module('CurrentUserModule');
    });

    it('should Initialize User from local storage if already exists there', inject(function (CurrentUser) {
        expect(CurrentUser.profile).toEqual(user);
    }))
});
Abhishek Jain

I have managed to find a better way as below. I was initially doing it the wrong way (feels stupid now). Rather than creating a mock module, I was creating a real module that was overriding the original one, and that's why it was impacting all tests. Now, I am creating a mock module and using $provide to override StorageFactory, which will impact only the current suite.

beforeEach(function () {
    angular.mock.module('StorageFactoryModule');
    module(function($provide) {
        $provide.value('StorageFactory', storageFactoryMock); 
    });
    angular.mock.module('CurrentUserModule');
});

EDIT: Refactored my code and made it more flexible by creating a function that accepts a user as parameter and creates modules based on the user passed.

var correctUser = {userid: 'abc', token: 'token'};
var defaultUser = {userid: '', token: ''};

function createStorageFactoryMock(userInStorage) {
        return {
            get: function () {
                return userInStorage;
            },
            put: function (key, newUser) {
                userInStorage = newUser;
            },
            remove: function () {
                userInStorage = undefined;
            }
        }
    }

function CreateUserModule(user = correctUser) {
        angular.mock.module('StorageFactoryModule');
        module(function ($provide) {
            $provide.value('StorageFactory', createStorageFactoryMock(user));
        });
        angular.mock.module('CurrentUserModule');
    }

Now in my tests, I can mock different module for different scenarios, and write my tests accordingly. Any feedback is welcome.

it('should Initialize User from storageFactory if already exists in storage', function () {
    CreateUserModule();
    inject(function (CurrentUser) {
        expect(CurrentUser.profile).toEqual(correctUser);
    });
});

it('should Initialize default user if user not present in storage', function () {
    CreateUserModule(null);
    inject(function (CurrentUser) {
        expect(CurrentUser.profile).toEqual(defaultUser);
    });
});

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Mocking a service in unit tests

From Java

Mocking $modal in AngularJS unit tests

From Dev

Angular Testing - Trouble mocking Service with $http dependency

From Dev

Mocking an asynchronous web service in Angular unit tests

From Java

Mocking AngularJS module dependencies in Jasmine unit tests

From Dev

AngularJS $resource service mocking, the right way

From Dev

Angular unit tests, mocking a service that is injected using $injector

From Dev

Sinon to stub AngularJS service in Tests

From Dev

AngularJS unit tests for database service

From Dev

Error with unit tests in AngularJS - controller with many dependency

From Dev

Understanding injection dependency in app and tests in AngularJS

From Dev

AngularJS, Mocha, Karma. testing controller, mocking service promise

From Dev

AngularJS, Mocha, Karma. testing controller, mocking service promise

From Dev

AngularJS and QUnit: Testing Controller with Service Dependency

From Dev

Best practice for dependency injection in an AngularJS service with TypeScript

From Java

AngularJS: Injecting service into a HTTP interceptor (Circular dependency)

From Dev

Difficulty in testing Angularjs controller with service as its dependency

From Dev

Mocking a dependency with AutoFixture

From Dev

Mocking dependency that has setListener(...)

From Dev

Mocha tests mocking function

From Java

Mocking HttpClient in unit tests

From Dev

TyphoonPatcher for mocking in unit tests

From Dev

Mocking ngModel in unit tests

From Dev

Mocking Googleads for Unit Tests

From Dev

TyphoonPatcher for mocking in unit tests

From Dev

Mocha tests mocking function

From Dev

Using service beans and dependency Injection in Geb Functional Tests

From Dev

AngularJS - unit tests - using original service from mock of same service?

From Dev

"$httpBackend.when is not a function" error when mocking AngularJS $httpBackend in Jasmine tests and using a decorator

Related Related

  1. 1

    Mocking a service in unit tests

  2. 2

    Mocking $modal in AngularJS unit tests

  3. 3

    Angular Testing - Trouble mocking Service with $http dependency

  4. 4

    Mocking an asynchronous web service in Angular unit tests

  5. 5

    Mocking AngularJS module dependencies in Jasmine unit tests

  6. 6

    AngularJS $resource service mocking, the right way

  7. 7

    Angular unit tests, mocking a service that is injected using $injector

  8. 8

    Sinon to stub AngularJS service in Tests

  9. 9

    AngularJS unit tests for database service

  10. 10

    Error with unit tests in AngularJS - controller with many dependency

  11. 11

    Understanding injection dependency in app and tests in AngularJS

  12. 12

    AngularJS, Mocha, Karma. testing controller, mocking service promise

  13. 13

    AngularJS, Mocha, Karma. testing controller, mocking service promise

  14. 14

    AngularJS and QUnit: Testing Controller with Service Dependency

  15. 15

    Best practice for dependency injection in an AngularJS service with TypeScript

  16. 16

    AngularJS: Injecting service into a HTTP interceptor (Circular dependency)

  17. 17

    Difficulty in testing Angularjs controller with service as its dependency

  18. 18

    Mocking a dependency with AutoFixture

  19. 19

    Mocking dependency that has setListener(...)

  20. 20

    Mocha tests mocking function

  21. 21

    Mocking HttpClient in unit tests

  22. 22

    TyphoonPatcher for mocking in unit tests

  23. 23

    Mocking ngModel in unit tests

  24. 24

    Mocking Googleads for Unit Tests

  25. 25

    TyphoonPatcher for mocking in unit tests

  26. 26

    Mocha tests mocking function

  27. 27

    Using service beans and dependency Injection in Geb Functional Tests

  28. 28

    AngularJS - unit tests - using original service from mock of same service?

  29. 29

    "$httpBackend.when is not a function" error when mocking AngularJS $httpBackend in Jasmine tests and using a decorator

HotTag

Archive