I'm trying to test a service documentViewer that depends on some other service authService
angular
.module('someModule')
.service('documentViewer', DocumentViewer);
/* #ngInject */
function DocumentViewer($q, authService) {
// ...
this.view = function(doc) {
//...
}
}
This is what my test looks like at the moment
it('test', inject(function($q) {
var doc = {
view: function() {
return $q.resolve(0);
}
};
var auth = {
refreshProfileData: function() {
return $q.resolve(0);
},
};
var viewer = createViewer(auth);
}));
function createViewer(auth) {
var viewer;
module({
authService: auth
});
inject(function(documentViewer) {
viewer = documentViewer;
});
return viewer;
}
The problem is I need to call inject to grab a $q, then use it to create my mocks, register my mocks with module, and then call inject again to grab the unit under test.
This results in
Error: Injector already created, can not register a module! in bower_components/angular-mocks/angular-mocks.js (line 2278)
I've seen lots of answers here on SO saying you can't call module after inject, but they don't offer any alternative to a scenario like the above.
What's the correct approach here?
PS: I'd like to avoid using beforeEach, I want each test to be self-contained.
module is used to define which modules will be loaded with inject and cannot be called after inject, this is chicken-egg situation.
The object accepted by module is used to define mocked services with $provide.value:
If an object literal is passed each key-value pair will be registered on the module via $provide.value, the key being the string name (or token) to associate with the value on the injector.
There can be no more than 1 function like createViewer that calls both module and inject. If this means that this kind of self-contained test is an antipattern, there is nothing that can be done about that. Angular testing works best with usual habits, including beforeEach and local variables.
In order to eliminate the dependency on $q, mocked service can be made a factory.
it('test', function () {
var authFactory = function ($q) {
return {
refreshProfileData: function() {
return $q.resolve(0);
},
};
};
// mocks defined first
module(function ($provide) {
$provide.factory('authService': authFactory);
});
var viewer;
inject(function(documentViewer) {
viewer = documentViewer;
});
// no module(...) is allowed after this point
var $q;
inject(function(_$q_) {
$q = _$q_;
});
var doc = {
view: function() {
return $q.resolve(0);
}
};
});
Related
I have a constant which is injected into a controller and I need to write a test which changes this constant and expects different results. I can use $provide to mock the constant but according to articles I've found online, I need to do it in the module declaration, which I believe is like this:
beforeEach(module("someModule"));
beforeEach(function () {
module(function ($provide) {
$provide.constant('someConstant', false);
});
});
I later load the controller like this:
function createController() {
view = $controller(
"someController",
{
$scope: $injector.get("$rootScope").$new()
});
}
Where $controller, $scope and $injector are all injected in my main beforeEach
This does provide the constant and it does change if I change the value in my beforeEach. But only for the entire test suite. I want to change this constant in a describe or an it but I'm not sure how. If I move the $provide down to a describe or it, I get the error:
Error: Injector already created, can not register a module!
I could just create a new file and that is probably what I'm going to do but is there a way I can $provide a dynamic value?
Lets consider such a code
controller
angular.module('someModule', [])
.controller('someController', function($scope, someConstant) {
$scope.someProvidedValue = someConstant;
})
and test for it
controller spec
describe('module', function () {
beforeEach(module("someModule"));
var createController;
beforeEach(inject(function (_$controller_, _$injector_) {
scope = _$injector_.get("$rootScope").$new()
createController = function createController(scope, obj) {
_$controller_("someController", {
$scope: scope,
someConstant: obj.someConstant
});
}
}))
it('someConstant', function () {
expect(scope.someProvidedValue).toBe(undefined)
createController(scope, {
someConstant: false
})
expect(scope.someProvidedValue).toBe(false)
})
it('someConstant', function () {
expect(scope.someProvidedValue).toBe(undefined)
createController(scope, {
someConstant: true
})
expect(scope.someProvidedValue).toBe(true)
})
})
In the mean time I'm looking for looks nicer solution.
I'm unit testing a provider in Jasmine, which relies on another provider. There's no configuration associated with this provider. When mocking a provider, I've read you're supposed to use something like
beforeEach(module(function ($provide) {
mockInjectedProvider = { };
$provide.value('injected', mockInjectedProvider );
}));
which works fine for me when injecting a custom provider into a service. When injecting them into a provider it doesn't work though. The code doesn't fail, but what gets executed when testing is the actual provider, not the mocked one. Abstracted example below.
var mockInjectedProvider;
beforeEach(function () {
module('myModule');
});
beforeEach(module(function ($provide) {
mockInjectedProvider = {
myFunc: function() {
return "testvalue"
}
}
};
$provide.value('injected', mockInjectedProvider );
}));
beforeEach(inject(function (_base_) {
baseProvider = _base_;
}));
it("injectedProvider should be mocked", function () {
var resultFromMockedProvider = baseProvider.executeMyFuncFromInjected();
expect(resultFromMockedProvider).toEqual("testvalue");
}); // Here instead of using my mock it executes the actual dependency
In the $provide.value statement I've tried including both injected and injectedProvider, as well as using $provide.provider and mocking a $get function on it but nothing seems to work. I just can't get it to mock away the actual provider. Abstracted base provider looks like this.
(function (ng, module) {
module.provider("base",
["injectedProvider", function (injectedProvider) {
this.executeMyFuncFromInjected= function() {
return injectedProvider.myFunc(); // let's say this returns "realvalue"
}
this.$get = function () {
return this;
};
}]
);
})(window.angular, window.angular.module("myModule"));
Everything in my code is working except the Jasmine mocking.
In this case is better to just mock the return value instead of the provider.
var mockInjectedProvider;
beforeEach(function () {
module('myModule');
});
beforeEach(inject(function (_injected_) {
spyOn(_injected_, "myFunc").and.returnValue("testvalue");
}));
beforeEach(inject(function (_base_) {
baseProvider = _base_;
}));
it("injectedProvider should be mocked", function () {
var resultFromMockedProvider = baseProvider.executeMyFuncFromInjected();
expect(resultFromMockedProvider).toEqual("testvalue");
}); // Here instead of using my mock it executes the actual dependency
I am trying to create a mock for testing a service that depends on another one managed by bower. The code for the Jasmine test is the following (full example at plunker):
describe('jsonrpc', function() {
'use strict';
var uuidMock, $httpBackend, jsonrpc;
beforeEach(module('jsonrpc', function ($provide) {
uuidMock = {};
uuidMock.generate = function () { return 0; };
$provide.value('uuid', uuidMock);
}));
beforeEach(inject(function(_jsonrpc_, _$httpBackend_) {
jsonrpc = _jsonrpc_;
$httpBackend = _$httpBackend_;
}));
it('should have created $httpBackend', function() {
expect($httpBackend.get).toBeDefined();
});
});
The 'jsonrpc' service provider is defined as follows:
angular.module('jsonrpc', ['uuid']).provider('jsonrpc', function() {
'use strict';
var defaults = this.defaults = {};
defaults.basePath = '/rpc';
this.$get = ['$http', 'uuid4', function($http, uuid4) {
function jsonrpc(options, config) {
... (etc) ...
When I try to mock the dependency of the 'jsonrpc' module on the 'uuid' module, I get the following error:
$injector:modulerr http://errors.angularjs.org/1.2.16/$injector/modulerr?p0=jsonrpc&p1=%5B%24injector%3Amodulerr%5D%20http%3A%2F%2Ferrors.angularjs.org%2F1.2.16%2F%24injector%2Fmodulerr%3Fp0%3Duuid%26p1%3D%255B%2524injector%253Anomod
What am I doing wrong when it comes to mock up that dependency?
What you're doing is not right because you're modifying the provider of the jsrpc module, not the uuid module, and you're only calling $provide.value to provide what should be a whole module (not a value)
If uuid4 is the only part of uuid that you need to mock, you can do
module('jsrpc', function($provide) {
$provide.service('uuid4', uuid4Mock)
});
Where uuid4Mock provides the behaviour only of that service, or whatever it is in there.
In a Jasmine test I have the following:
CommentMock = function() {};
CommentMock.prototype.save = function() {
// stuff
};
spyOn( CommentMock.prototype, 'save' ).andCallThrough();
However, I'm getting this error:
Failure/Error: save() method does not exist
In an Angular controller I have this:
$scope.newComment = new Comment();
$scope.processComment = function( isValid ) {
if ( isValid ) {
Comment.save( $scope.newComment )
.$promise
.then(
function() {
// success stuff
},
function() {
// error junk
}
);
}
};
If Comment is a service I would mock it like this instead:
CommentMock = {}
CommentMock.save = function() {
// stuff
};
spyOn( CommentMock, 'save' ).andCallThrough();
But actually I wouldnt mock it like this at all. I would allow the service to be injected into the unit test and then intercept the service call using the spyOn method of jasmine.
var Comment, $rootScope, $controller; //... maybe more...
beforeEach(inject(function(_$rootScope_, _Comment_, _$controller_ //,... everything else) {
$controller = _$controller_;
$rootScope = _$rootScope_;
Comment = _Comment_;
}));
function setupController() {
spyOn(Comment, 'save').andCallThrough();
controller = $controller('YOURCONTROLLERSNAME', {
$scope: $scope,
Comment: Comment
}
}
Code is super simplified and wont work straight like this but its the overall idea...
Some other unit testing links I wrote:
Mocking Controller Instantiation In Angular Directive Unit Test
Unit testing in AngularJS - Mocking Services and Promises
I'm trying to test a simple service but I'm getting an Unkown Provider error. Here is the service definition:
var sessionManager = angular.module('MyApp.SessionManager', ['ngResource']);
sessionManager.factory('UserService', function($resource) {
var UserService = $resource('/api/users/:key', {}, {
getNewUUID: {
method: 'GET',
params: {
action: 'getNewUserUUID'
}
}
});
return UserService;
});
and here is the test:
describe('Testing SessionManager', function() {
var userService;
beforeEach(function() {
module('MyApp.SessionManager');
inject(function($injector) {
userService = $injector.get('UserService');
});
});
it('should contain a UserService', function() {
expect(userService).toBeDefined();
});
});
I can't seem to see the problem, I know that the UserService javascript file is being called because I can get a console log at the top of the file, however if I put it in the service definition I don't see it get called. So for some reason it's like Angular is not instantiating my service?
I realized that the problem was my module MyApp.SessionManager was being replaced because I thought you could declare dependencies every time it was reopened to add a module. The code above is fine if of course the service is actually surviving up until the tests.