Angular & Jasmine: how to inject services with dots in their names - angularjs

I've got a service that's defined this way:
angular.module("myApp")
.factory("myService.foo", function () {
// utterly delightful code
});
I'm using Karma and Jasmine for testing. In the test, I am doing something like this for most of my server tests:
describe('Service: someService', function () {
// load the service's module
beforeEach(module('myApp'));
// instantiate service
var _someService;
beforeEach(inject([function (someService) {
_someService = someService;
}]));
it('should do something', function () {
expect(!!_someService).toBe(true);
});
});
When I try to do the same with a service named something like "myService.foo" it throws an error (of course):
describe('Service: myService.foo', function () {
// load the service's module
beforeEach(module('myApp'));
// instantiate service
var _myService;
beforeEach(inject([function (myService.foo) {
_myService = myService.foo;
}]));
it('should do something', function () {
expect(!!_myService).toBe(true);
});
});
Because of the obvious problem with the dot syntax making angular unable to infer the service name. How can I inject this service to test it? Is there some alternate syntax I'm missing?

You can use the array notation, for example:
var _myService;
beforeEach(inject(['myService.foo', function (myService) {
_myService = myService;
}]));
Note (from documentation):
It is important that the order of the string identifiers in the array
is the same as the order of argument names in the signature of the
factory function. Unless the dependencies are inferred from the
function signature, it is this array with IDs and their order that the
injector uses to determine which services and in which order to
inject.

Related

Spying on a wired service in angular karma test

Is it possible to spy on a service in a karma test that was wired by Angular?
Example: myService is the unit under test. thirdParty stands for a third party service that should be spied on.
.service('thirdParty', function() {
return {
hello: function() {
return 'hello world';
}
}
})
.service('myService', function(thirdParty) {
return {
world: function() {
return thirdParty.hello();
}
}
})
In my karma test I would like to spy on thirdParty service and call the real service:
describe('spy', function() {
var thirdParty, myService;
beforeEach(inject(function(_thirdParty_, _myService_) {
myService = _myService_;
thirdParty = _thirdParty_;
spyOn(thirdParty, 'hello').andCallThrough();
}));
it('should be called in myService', function() {
expect(thirdParty.hello).toHaveBeenCalled();
expect(myService.world()).toBe('hello world');
});
})
The point is that my test should assert that
a specific method of the third party service has been called inside myService
the third party service doesn't change its internal behaviour that would lead to a an exception or unexpected result (e.g. after a library update)
The myService.world() assertion just works but as I expect myService doesn't operate on the spied thirdParty service.
The result is:
Expected spy hello to have been called.
In some tests I'm already mocking third party services with a provider and a bare mock.
So I tried to create a spying instance of cacheFactory that comes with angular-cache:
beforeEach(module('angular-cache'));
beforeEach(module(function($provide, $injector, CacheFactoryProvider) {
//CacheFactoryProvider requires $q service
var q = $injector.get('$q');
var cacheFactory = CacheFactoryProvider.$get[1](q);
spyOn(cacheFactory, 'createCache').andCallThrough();
$provide.factory('CacheFactory', cacheFactory);
}));
Now I`m facing the chicken-and-egg problem:
Error: [$injector:modulerr] Failed to instantiate module function ($provide, $injector, CacheFactoryProvider) due to:
Error: [$injector:unpr] Unknown provider: $q
I know that this example can't work but because of lack of knowledge of the internals how Angular is actually instantiating and wiring services I would like to ask the community whether my test approach is possible or even sane. Thanks for help.
Instead of
it('should be called in myService', function() {
expect(thirdParty.hello).toHaveBeenCalled();
expect(myService.world()).toBe('hello world');
});
the test should be
it('should be called in myService', function() {
expect(myService.world()).toBe('hello world');
expect(thirdParty.hello).toHaveBeenCalled();
});
Indeed, the thirdParty.hello method won't have been called until you actually call myService.world().

jasmine: Mocking angular service yields timeout

I am trying to test an angular service. The service is using another service and I would like to mock that one.
Let's say I have a service myService that depends on the service myOtherService. When just testing the application I get
Unknown provider: myOtherServiceProvider <- myOtherService <- myService
This is because I have not included the file specifing the service in the unit test. And I also don't want because I would like to mock this service.
I have come across this reference and have tried to with something like that:
describe('Service: myService', function() {
var myService;
beforeEach(function(){module('myApp');});
beforeEach(function($provide){
module(function($provide) {
$provide.service('myOtherService', function() {
this.doSomething = function(){
//...
}
});
});
});
beforeEach(inject(function(_myService_) {
myService = _myService_;
}));
describe('Duck Typing', function() {
it('should contain a doSomething() API function', function(){
expect(angular.isFunction(myService.doSomething)).toBe(true);
});
});
});
But when running the unit test I get the following error:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
So I would like to learn what is wrong here and how I can mock the service dependency correctly.
Thanks!

Unit-Testing with Karma not calling functions

I am trying to perform unit testing with Karma. I have done everything according to the documentation. When I write this part of the test that follows it never calls the last two functions.
it('should create the mock object', function (done) {
service.createObj(mockObj)
.then(test)
.catch(failTest)
.finally(done);
});
var test = function() {
expect(2).toEqual(1);
};
var failTest = function(error) {
expect(2).toEqual(1);
};
Try to inject into your beforeEach function rootScope. For example like this:
var rootScope;
beforeEach(inject(function (_$rootScope_) {
rootScope = _$rootScope_.$new();
//other injections
}));
and next invoke $digest() after your service method:
it('should create the mock object', function (done) {
service.createObj(mockObj)
.then(test)
.catch(failTest)
.finally(done);
rootScope.$digest();
});
Install angular-mocks module.
Inject module with module in beforeEach.
Inject your service with inject function in beforeEach.
Use $httpBackend to simulate your server.
Do, not forget, to make it, sync. with $http.flush().

Reusing angular mocks in Jasmine tests using $provide

I wish to reuse my mocks instead of having to set them up in every unit test that has them as dependency. But I'm having a hard time figuring out how to inject them properly.
Here's my attempt at unit test setup, which of course fails because ConfigServiceMockProvider doesn't exist.
describe('LoginService tests', function () {
var LoginService;
beforeEach(module('mocks'));
beforeEach(module('services.loginService', function ($provide, _ConfigServiceMock_) {
$provide.value("ConfigService", _ConfigServiceMock_);
/* instead of having to type e.g. everywhere ConfigService is used
* $provide.value("ConfigService", { 'foobar': function(){} });
*/
});
beforeEach(inject(function (_LoginService_) {
LoginService = _LoginService_;
});
}
ConfigServiceMock
angular.module('mocks').service('ConfigServiceMock', function() {
this.init = function(){};
this.getValue = function(){};
}
I realize I probably could have ConfigServiceMock.js make a global window object, and thereby not needing to load it like this. But I feel there should be a better way.
Try something like this:
describe('Using externally defined mock', function() {
var ConfigServiceMock;
beforeEach(module('mocks'));
beforeEach(module('services.configService', function($provide) {
$provide.factory('ConfigService', function() {return ConfigServiceMock;});
}));
beforeEach(module('services.loginService'));
beforeEach(inject(function (_ConfigServiceMock_) {
ConfigServiceMock = _ConfigServiceMock_;
}));
// Do not combine this call with the one above
beforeEach(inject(function (_LoginService_) {
LoginService = _LoginService_;
}));
it('should have been given the mock', function() {
expect(ConfigServiceMock).toBeDefined('The mock should have been defined');
expect(LoginService.injectedService).toBeDefined('Something should have been injected');
expect(LoginService.injectedService).toBe(ConfigServiceMock, 'The thing injected should be the mock');
});
});
According to this answer, you have to put all of your calls to module before all of your calls to inject.
This introduces a bit of a catch-22 because you have to have the reference to your ConfigServiceMock (via inject) into the spec before you can set it on the LoginService (done in the module call)
The work-around is to set an angular factory function as the ConfigService dependency. This will cause angular to lazy load the service, and by that time you will have received your reference to the ConfigServiceMock.

Injecting mock angular service dependencies

I have a service, 'Inputs', defined in module 'Puts', that depends on a second service, 'InputCreator'. I need to stub the InputCreator service in order to test the Inputs service.
As I understand the answer here, I should create a module containing my stub service, then create a new 'Test' module, specifying the module under test and then the stub module as dependencies. And then pull the service from the injector. Like so:
beforeEach(function() {
angular.module.('Puts'); // contains the service 'Inputs'
angular.module('Mocks',[])
.service('InputCreator',function(){
var mockInputs = {
//stubbed behaviour goes here
};
return mockInputs;
});
});
angular.module('Test',['Puts', 'Mocks'];
inject(function($injector){
Inputs = $injector.get('Inputs');
});
});
However, the injector function responds with 'unknown InputsProvider <- Inputs'.
Where have I gone astray?
Thanks!
Having figured this out, I thought I'd answer my own question. The big mistake above was using angular.module rather than angular.mock.module, that is convenience referenced as module by angular-mock. They aren't the same thing at all!
Additionally, it's enough to initialize the mock service with angular.mock.module, so long as you do it before you initialize the module under test. There's no need for this 'wrapping the modules in a third module' business as suggested in the question linked above. To wit:
describe("Test Service", function() {
var TestService, getvaluestub;
beforeEach(function() {
// create mock service
var mock = {getvalue:function(){}}
angular.module('dependencymodule',[])
.service('dependencyservice',function () {
return mock;
});
//mock the function we are stubbing, (that, in this case, returns value 4)
getvaluestub = sinon.stub(mock,'getvalue')returns(4);
//instantiate your mock service
module('dependencymodule');
//instantiate the module of the service under test,
//that depends on 'dependencyservice' mocked above
//(ie - testmodule includes the service 'testservice')
module('testmodule');
//inject your test service for testing
inject(function ($injector) {
TestService = $injector.get('testservice');
})
//tests go here.....
If the dependency module already exists, you could either still do all of the above, or you could acquire the service from the $injector, insert your spies and stubs, and >then< instantiate the service under test. It's important that the spies/stubs are set up >before< the dependent service is instantiated, or it will be instantiated without them. It looks like this:
describe("Test Service", function() {
var TestService, DependencyService, getvaluestub;
beforeEach(function() {
// these modules are specified in the application
module('dependencymodule');
module('testmodule');
inject(function ($injector) {
DependencyService = $injector.get('testservice');
getvaluestub = sinon.stub(DependencyService,'getvalue').returns(4);
OtherService = $injector.get('otherservice');
})
});
// test go here
So, there you go. Hopefully this is useful to someone who searches for 'Injecting mocks into angular services'.

Resources