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.
Related
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);
}
};
});
The module definition
var module = angular.module('test', []);
module.provider('client', function() {
this.$get = function($http) {
return {
foo: function() {
return $http.get('foo');
}
}
}
});
module.factory('service', ['client', function(client) {
return {
bar: function() {
return client.foo();
}
}
}]);
Basically, client is a wrapper for http calls, and service is a wrapper around the client basic features.
I'm unit testing both the provider and the service with karma+jasmine. The provider tests run as expected, but i have a problem with the service tests:
describe('service test', function(){
var service = null;
beforeEach(function(){
module('test')
inject(function(_service_, $httpBackend, $injector) {
service = _service_;
$httpBackend = $injector.get('$httpBackend');
});
});
it('should invoke client.foo via service.bar', function() {
$httpBackend.expect("GET", "foo");
service.bar();
expect($httpBackend.flush).not.toThrow();
});
});
I get Expected function not to throw, but it threw Error: No pending request to flush !.. When testing the provider with the same way, this test passes. Why?
When you are testing your service, you need to mock the client and inject that mock instead of the real client. Your mock can be in the same file if you only expect to use it for testing this service or in a separate file if you'll use it again elsewhere. Doing it this way does not require the use of $httpBackend (because you are not actually making an http call) but does require using a scope to resolve the promise.
The mock client:
angular.module('mocks.clientMock', [])
.factory('client', ['$q', function($q) {
var mock = {
foo: function() {
var defOjb = $q.defer();
defOjb.resolve({'your data':'1a'});
return defOjb.promise;
}
};
return mock;
}]);
Using the mock:
describe('service test', function(){
var service, mock, scope;
beforeEach(function(){
module('test', 'mocks.clientMock');
inject(function(_service_, $rootScope) {
service = _service_;
scope = $rootScope.$new();
});
});
it('should invoke client.foo via service.bar', function() {
spyOn(client, 'foo').and.callThrough();
service.bar();
scope.$digest();
expect(client.foo).toHaveBeenCalled();
});
});
I am trying to test some AngularJS factories with Jasmine. It works fine for factories that don't have any dependencies. One of my factories uses Angular Material's $mdToast as dependency.
The factory:
(function() {
'use strict';
angular
.module('myModule')
.factory('ToastFactory', ToastFactory);
ToastFactory.$inject = ['$mdToast'];
function ToastFactory($mdToast) {
var service = {
showToast1: showToast1,
showToast2: showToast2
};
return service
function showToast1() {
return $mdToast.show($mdToast.build({
templateUrl: 'path'
}));
}
function showToast2() {
return $mdToast.show($mdToast.build({
templateUrl: 'path'
}));
}
}
})();
And here is one of the working tests for another factory without dependencies.
describe('myFactory', function() {
//Injector Service
var $injector;
//Set Module
beforeEach(function() {
angular.module('myModule');
});
//Inject injector service
beforeEach(inject(function() {
$injector = angular.injector(['myModule']);
}));
describe('SampleTest', function() {
it('should be true', function() {
//Arrange
var factory = $injector.get('myFactory');
//Act
var res = factory.testMethod();
//Assert
expect(res).toBe(true);
});
});
})
I know how to do it for controllers, but not for factories.
I am trying to test an angular factory using jasmine and mocha however I keep getting undefined errors.
here is my factory:
(function() {
'use strict';
angular.module('app').factory('appFactory', appFactory);
appFactory.$inject = ['$http','$q'];
function appFactory($http,$q) {
return {
get: get
};
function get(someData) {
//....
}
}
})();
and here is my unit test using jasmine and mocha:
'use strict';
describe('Factory: appFactory', function() {
var http, appFactory;
var $rootScope, $scope;
beforeEach(function() {
angular.module('app');
});
beforeEach(inject(function($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
}));
beforeEach(inject(function() {
var $injector = angular.injector(['ngMock', 'ng', 'app']);
appFactory = $injector.get('appFactory');
}));
it('should be defined', inject( function() {
alert(appFactory);
}));
});
so my alert looks something like:
Object{get: function get(someData) {...}
how do I go about testing the functionality of my factory if this is returning an object?
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