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);
}))
});
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.
Comments