I've been trying to mock a provider (for use in another provider) and couldn't manage to get it mocked.
In the test, I see the actual provider function and not the mocked one.
Here is the (simplified) code:
First, the setup: (I removed anything that isn't needed):
function ServiceToMock() {
this.functionMock = function () {
// do something...
};
this.$get = function () {
// doesn't matter
};
}
module('theModule').provider('serviceToMock', ServiceToMock);
function ServiceThatUsesMock(serviceToMock) {
serviceToMock.functionMock(); // I want the mocked function to run here but the actual one does
this.$get = function () {
// doesn't matter...
};
}
module('theModule').provider('serviceThatUsesMock', ServiceThatUsesMock);
As you can see, two services defined with .provider(), the second is dependent on the first.
And the unit test:
let functionMock = jasmine.createSpy();
module('theModule');
module(function ($provide) {
$provide.provider('serviceToMock', function () {
this.functionMock = functionMock;
this.$get = () => ({});
this.test = 'aaa';
});
});
var serviceThatUsesMock;
beforeEach(inject(function (_serviceThatUsesMock_) {
serviceThatUsesMock = _serviceThatUsesMock_;
}));
it('should register for update failure', function () {
expect(functionMock).toHaveBeenCalled();
});
The test fails because the real function gets called and not the mocked one.
What Am I Doing wrong?
Thanks!
Related
Setup: Karma, Browserify, Jasmine, Angular.
I'm trying to test an Angular service which, in turn, uses another service, which I'd like to mock.
I have the feeling that here I am getting another instance of my ItemLocalStorageService, created before mock injection took place.
ItemLocalStorageService.js
'use strict';
var _ = require('underscore'),
LOCALSTORAGE_LIST_KEY = 'items.list',
items;
module.exports = [
'StoreService',
function (StoreService) {
var getAll = function () {
if (StoreService.enabled) {
items = StoreService.get(LOCALSTORAGE_LIST_KEY);
}
return items;
};
var saveAll = function (latestItems) {
if (StoreService.enabled) {
StoreService.set(LOCALSTORAGE_LIST_KEY, latestItems);
}
items = latestItems;
};
return {
getAll: getAll,
saveAll: saveAll
};
}
];
ItemLocalStorageServiceTest.js
// Main application file. This is loaded here
// instead of being added to Karma's files array
// because of the Browserify setup.
require('scripts/app');
var mock = require('angular').mock;
describe('ItemLocalStorageService', function () {
var ItemLocalStorageService,
items,
mockStoreService;
beforeEach(mock.module('myApp'));
beforeEach(mock.module('myTemplates'));
beforeEach(function () {
items = [ {}, {}, {} ];
mockStoreService = {
'store': {
enabled: false,
get: jasmine.createSpy('store.get').and.returnValue(items),
set: jasmine.createSpy('store.set')
}
};
// Either of these two should work, I believe.
// 1
// mock.module({ 'StoreService': mockStoreService })''
// 2
mock.module(function($provide) {
$provide.value('StoreService', mockStoreService);
});
});
describe('Local storage', function () {
it('should return items from local storage', function () {
mock.inject(function (_ItemLocalStorageService_) {
ItemLocalStorageService = _ItemLocalStorageService_;
});
expect(ItemLocalStorageService.getAll()).toEqual(items);
expect(mockStoreService.store.get).toHaveBeenCalled();
});
});
});
Both expectations fail. The output of getAll is the actual content of the browser's local storage, not the mock output I provide. I'm obviously doing something wrong, so I'll gladly accept any pointers to lead me on the right way.
How to create Jasmine unit test for one function in AngularJS service provider. I want to create mock data for myObject and test function getObjectShape() with that mock data as parameter. How to achieve that?
(function () {
'use strict';
angular.module('objectShapes')
.provider('shapesResolver', shapesResolver);
function shapesResolver() {
this.$get = function () {
return resolver;
};
function resolver(myObject) {
var service = {
getObjectShape: getObjectShape
};
function getObjectShape() {
return myObject.Shape;
}
}
}
})();
Here's a skeleton of a test for your service.
describe('shapesResolver service', function() {
var shapesResolver;
beforeEach(module('objectShapes'));
beforeEach(inject(function(_shapesResolver_) {
shapesResolver = _shapesResolver_;
}));
it('should do something, but what?', function() {
var mockMyObject = {};
shapesResolver(mockMyObject);
// shapesResolver doesn't return anything, and doesn't
// have any side effect, so there's nothing to test.
expect(true).toBeTruthy();
});
});
I am unit testing my first filter, which seems easy enough but I keep getting
Unknown provider: activeProfileProvider <- activeProfile <- pbRoles <- rolesFilter
The activeProfile is a consent that is a dependency of pbRoles. My understanding of constants is limited and I cant seem to find a way to include it. I have tried adding the config file where the constants are declared as well as mocking the constant in the test to no avail.
does anyone know if there is a specific way to do this? or does my problem lay else where?
My filter:
angular.module('pb.roles.filters')
.filter('roles', ['pbRoles', function (pbRoles) {
return function (input) {
if (!input) {
return "None";
} else {
return pbRoles.roleDisplayName(input);
}
};
}]);
My test:
describe('RolesController', function () {
beforeEach(module('pb.roles'));
beforeEach(module('pb.roles.filters'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var rolesFilter;
var mockActiveProfile = {};
beforeEach(inject(function (_rolesFilter_) {
activeProfile = mockActiveProfile;
rolesFilter = _rolesFilter_;
}));
var pbRoles = {
roleDisplayName: function (input) {
return input
}
};
describe('role: filter', function () {
it('should return None if called with no input', function () {
expect(rolesFilter()).toBe('None');
});
it('should call roleDisplayName with input', function () {
expect(roles('Hello')).toBe('Hello');
});
});
});
have also tried mocking the constent like so:
module(function ($provide) {
$provide.constant('activeProfile', function () {
pbGlobal.activeProfile = {
profile: ""
}
});
});
mocking the providers at the top of the page like so worked:
beforeEach(module(function ($provide) {
$provide.constant('organizationService', function () {
});
$provide.service('activeProfile', function () {
activeProfile = {
profile: ""
}
});
}));
http://www.sitepoint.com/mocking-dependencies-angularjs-tests/ was a huge help.
My full working test if anyone is curious:
describe('RolesController', function () {
var mockPbRoles = {
roleDisplayName: function (input) {
return "it worked!"
}
};
beforeEach(module(function ($provide) {
$provide.value('pbRoles', mockPbRoles);
$provide.constant('organizationService', function () {});
$provide.service('activeProfile', function () { });
}));
beforeEach(module('pb.roles'));
beforeEach(module('pb.roles.filters'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var rolesFilter;
beforeEach(inject(function (_rolesFilter_) {
rolesFilter = _rolesFilter_;
}));
describe('role: filter', function () {
it('should return None if called with no input', function () {
expect(rolesFilter(false)).toBe('None');
});
it('should call roleDisplayName with input', function () {
result = rolesFilter(true);
expect(result).toEqual("it worked!");
});
});
});
Is there anyway to change the constant value that gets sent to the config function of the module for a Unit Test?
I have the following (fiddle here):
//--- CODE --------------------------
var module = angular.module("myApp", []);
module.constant("myConstant", "foo");
module.provider("awesomeStuff", function () {
var value;
this.setValue = function (val) {
value = val;
};
this.$get = function () {
return {
myValue: value
};
};
});
module.config(function (myConstant, awesomeStuffProvider) {
//want to mock myConstant
awesomeStuffProvider.setValue(myConstant);
});
//--- SPECS -------------------------
describe("foo", function() {
beforeEach(angular.mock.module("myApp", function ($provide) {
//Attempt to override the myConstant value that gets passed to config
$provide.constant("myConstant", "bar");
}));
it("has a value of bar", inject(function(awesomeStuff, $injector) {
expect($injector.get("myConstant")).toBe("bar");
expect(awesomeStuff.myValue).toBe("bar");
}));
});
I know this is a trivial example, but I want to know if its possible to get a different constant to be injected into the config... I know it is possible to get a reference to the provider and call setValue function from unit test (i.e. configuring provider via this SO post), but this is not what I'm looking for.
Thanks for any help.
Consider the following:
beforeEach(angular.mock.module("myApp"));
This will load the module and execute the registered config functions.
In your case you have:
beforeEach(angular.mock.module("myApp", function ($provide) {
//Attempt to override the myConstant value that gets passed to config
$provide.constant("myConstant", "bar");
}));
What happens now is basically the same as if you had the following:
var module = angular.module("myApp", []);
... Constant and Provider omitted ...
module.config(function(myConstant, awesomeStuffProvider) {
awesomeStuffProvider.setValue(myConstant);
});
module.config(function($provide) {
$provide.constant("myConstant", "bar");
});
As the registered config functions will be executed in order of registration this will not have the desired result.
If you need to mock a constant before it's used in any config function I would recommend to put it in a separate module.
Depending on your use case, you can now either:
Create two versions of this module - one for production and one for the tests
Create only the real version and mock the constant in the test
The second case would look something like this:
App:
angular.module("configuration", []).constant("myConstant", "foo");
var module = angular.module("myApp", ["configuration"]);
module.provider("awesomeStuff", function () {
var value;
this.setValue = function (val) {
value = val;
};
this.$get = function () {
return {
myValue: value
};
};
});
module.config(function (myConstant, awesomeStuffProvider) {
awesomeStuffProvider.setValue(myConstant);
});
Test:
describe("foo", function () {
beforeEach(function () {
angular.mock.module("configuration", function ($provide) {
$provide.constant("myConstant", "bar");
});
angular.mock.module("myApp", function () {
// Something else
});
});
it("has a value of bar", inject(function (awesomeStuff, $injector) {
expect($injector.get("myConstant")).toBe("bar");
expect(awesomeStuff.myValue).toBe("bar");
}));
});
JSFiddle: http://jsfiddle.net/f0nmbv3c/
I have created an Angular factory that has methods which handle saving code to a server. One of the factory methods contains a third party object which has a method which does the actual callout. I would like to test this code, but I can't work out how to mock out the third party object.
I have set up a plunker with a Jasmine test.
My aim for this test is just to successfully get the code to use my mock object rather than the ThirdPartySavingUtils object. Is that possible?
var app = angular.module("MyApp", []);
app.factory("SavingUtils", function() {
return {
saveStuff: function() {
if(typeof ThirdPartySavingUtils !== "undefined") {
return ThirdPartySavingUtils.value;
}
}
};
});
this is my jasmine tests
describe("Mocking Test", function() {
var ThirdPartySavingUtilsMock;
var SavingUtils;
beforeEach(function() {
angular.mock.module("MyApp", function($provide) {
ThirdPartySavingUtilsMock = {
value: "I am the mock object"
};
$provide.value("ThirdPartySavingUtils", ThirdPartySavingUtilsMock);
});
inject(function(_SavingUtils_) {
SavingUtils = _SavingUtils_;
});
});
it("should run without throwing an exception", function() {
expect(true).toBe(true);
});
it("should mock out ThirdPartySavingUtils with ThirdPartySavingUtilsMock", function() {
var result = SavingUtils.saveStuff();
expect(result).toEqual("I am the mock object");
});
});
You have a few options really but more than likely you would need to do both.
1) You could create an angular service which wraps this third party object - this way you get a nice abstraction incase you ever need to change the third party object.
2) You could use a mocking framework like http://sinonjs.org/ which enable you to mock methods out and do asserts like calledOnce etc.
Here is a link to a mocked test using sinon test.
You can bascially see sinon is used as a sandbox to mock out an object methods. Sinon provides extra propeties to those mocked methods so you can assert if they were called, the parameters they were called with even the order of the calls. It is a really, really essential testing tool.
describe('validationManager', function () {
beforeEach(inject(function ($injector) {
sandbox = sinon.sandbox.create();
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
$q = $injector.get('$q');
defer = $q.defer();
validator = $injector.get('validator');
validationManager = $injector.get('validationManager');
sandbox.stub(validator, 'makeValid');
sandbox.stub(validator, 'makeInvalid');
sandbox.stub(validator, 'getErrorMessage').returns(defer.promise);
setModelCtrl();
}));
afterEach(function () {
sandbox.restore();
setModelCtrl();
});
it('should be defined', function () {
expect(validationManager).to.exist;
});
describe('validateElement', function () {
it('should return if no $parsers or $formatters on the controller', function () {
validationManager.validateElement(modelCtrl);
expect(validator.makeValid.called).to.equal(false);
expect(validator.makeInvalid.called).to.equal(false);
});
});
EDIT -----------------------
Here this put into practice for your code (I haven't run this but it give the general idea).
(function (angular, ThirdPartyApi) {
'use strict';
var app = angular.module('MyApp', []);
app.factory('thirdPartApi', [
function () {
return {
save: ThirdPartyApi.save,
value: ThirdPartyApi.value
};
}
]);
app.factory('SavingUtils', [
'thirdPartApi',
function (thirdPartApi) {
var getValue = function () {
return thirdPartApi.value;
},
save = function (item) {
return thirdPartApi.save(item);
};
return {
save: save,
getValue: getValue
};
}
]);
}(angular, window.ThirdPartyApi));
The tests.....
(function (angular, sinon) {
'use strict';
describe('MyApp.SavingUtils', function () {
var sandbox, thirdPartyApi, SavingUtils, thirdPartyApiValue = 2;
beforeEach(inject(function ($injector) {
sandbox = sinon.sandbox.create();
thirdPartyApi = $injector.get('thirdPartyApi');
SavingUtils = $injector.get('SavingUtils');
// stub the method and when called return a simple object or whatever you want
sandbox.stub(thirdPartyApi, 'save').returns({ id: 1});
sandbox.stub(thirdPartyApi, 'value', function () {
return thirdPartyApiValue;
});
}));
afterEach(function () {
// This removes those stubs and replace the original methods/values
sandbox.restore();
});
describe('save', function () {
it('should return call the save method on thirdPartyApi', function () {
var item = {};
SavingUtils.save(item);
expect(thirdPartyApi.save.calledOnce).to.equal(true);
});
});
describe('getValue', function () {
it('should return value of value property on thirdPartyApi', function () {
var result = SavingUtils.getValue();
expect(result).to.equal(thirdPartyApiValue);
});
});
});
}(angular, sinon));