Yet another 'Unknown provider' for an AngularJS service - javascript

I actually hate to be that guy, but I've been sitting with this
problem for some days now. I have these three files as a part of a
larger angularjs application. I can not get even this rudimentary test
to pass (or even work). I've been comparing files within the project,
I've read on-line (tried all those ways people have suggested). I have
even written the files from scratch a few times. I'm probably not able
to see my error anymore. I guess this is easier to spot (right away)
for a back-seat driver.
I'd be most appreciative for any help.
The output from gulp/karma
PhantomJS 2.1.1 (Linux 0.0.0) SiteDescriptionService the service should be defined FAILED
Error: [$injector:unpr] Unknown provider: SiteDescriptionServiceProvider <- SiteDescriptionService
http://errors.angularjs.org/1.5.8/$injector/unpr?p0=SiteDescriptionServiceProvider%20%3C-%20SiteDescriptionService (line 4511)
bower_components/angular/angular.js:4511:86
getService#bower_components/angular/angular.js:4664:46
bower_components/angular/angular.js:4516:48
getService#bower_components/angular/angular.js:4664:46
injectionArgs#bower_components/angular/angular.js:4688:68
invoke#bower_components/angular/angular.js:4710:31
workFn#bower_components/angular-mocks/angular-mocks.js:3085:26
loaded#http://localhost:8080/context.js:151:17
inject#bower_components/angular-mocks/angular-mocks.js:3051:28
app/service/sitedescriptor-service-test.js:10:19
app/service/sitedescriptor-service-test.js:4:13
global code#app/service/sitedescriptor-service-test.js:1:9
Expected undefined to be truthy.
app/service/sitedescriptor-service-test.js:17:32
loaded#http://localhost:8080/context.js:151:17
The module declaration
(function(){
'use strict';
angular.module('application.service', []);
})();
The service itself
(function () {
angular.module('application.service')
.service('SiteDescriptorService',
['$http', '$q', function ($http, $q) {
var lastRequestFailed = true,
promise,
items = [];
return {
name: 'SiteDescriptorService',
getItems: function () {
if (!promise || lastRequestFailed) {
promise = $http.get('site.json').then(
function (response) {
lastRequestFailed = false;
items = response.data;
return items;
}, function (response) { // error
lastRequestFailed = true;
return $q.reject(response);
});
}
return promise;
}
};
}]
);
})();
and the test
describe('SiteDescriptionService', function() {
'use strict';
describe('the service', function() {
var service, httpBackend;
beforeEach(module('application.service'));
beforeEach(inject(function(_SiteDescriptionService_, $httpBackend) {
service = _SiteDescriptionService_;
httpBackend = $httpBackend;
console.log(service);
}));
it('should be defined', function() {
expect(service).toBeTruthy();
});
});
});
Cheers
Mats

Looks like you just use incorrect name when injecting dependency, should be 'SiteDescriptorService' and not 'SiteDescriptionService'

Related

Unit Testing a Keycloak-enabled Angular app

I'm trying to write a test suite for an Angular app that uses Keycloak for authentication.
However, as Keycloak requires you to manually bootstrap Angular and set up a few interceptors, I'm unable to fire any test due to the following error:
Error: $injector:unpr
Unknown Provider: AuthProvider <- Auth <- authInterceptor <- $http <- $templateRequest <- $route
This is the code for the interceptor that raises the error:
angular.module('MPMReportGenerator')
.factory('authInterceptor', function authInterceptor ($q, Auth, $log) {
return {
request: function (config) {
var deferred = $q.defer()
Auth.updateToken(5).success(function () {
config.headers = config.headers || {}
config.headers.Authorization = 'Bearer ' + Auth.token
deferred.resolve(config)
}).error(function () {
deferred.reject('Failed to refresh token')
})
$log.info(deferred.promise)
return deferred.promise
}
}
})
My thinking is that I should mock the interceptor and just have it return the request.
However, I fail to see how I could do that, since this interceptor is never injected anywhere as a dependency, it's simply declared with the block above and that's it. My understanding of mocked services is that they need to be injected somewhere to be mocked.
My implementation of Keycloak into Angular comes straight from their examples, if that helps.
Edit
I've been trying to inject a mocked Auth module into the service I'm writing a test for, but still no change.
I'm very new to unit testing in general, so I'm a bit lost trying to track this down. I feel like I know where the issue is, but not how to solve it (The Auth service is added during the bootstrap of the app, I need to mock it for things to work, but it seems I don't know how/where to mock it properly)
Here's the whole testing code:
describe('Services', function () {
'use strict'
beforeEach(module('MPMReportGenerator'))
module(function ($provide) {
$provide.factory('Auth', function () {
return null
})
})
var sectionService, $httpBackend, mockAuth
beforeEach(inject(function (_sectionService_, _$httpBackend_, Auth) {
sectionService = _sectionService_
$httpBackend = _$httpBackend_
mockAuth = Auth
}))
it('should get sections', function () {
$httpBackend.expect('GET', '/MPMReportGenerator/api/categories/all').respond(200)
sectionService.getSections()
expect($httpBackend.flush).not.toThrow()
})
})
Edit 2
I've managed to get past my initial error by making a mock version of Auth.
I am now facing issues implementing a mock version of Keycloak's Javascript library.
My current mock code is as follow:
beforeEach(module(function ($provide) {
$provide.factory('Auth', function ($q) {
return {
updateToken: function (minValidity) {
return {
success: function (fn) {
var deferred = $q.defer()
deferred.resolve('')
fn(deferred.promise)
},
error: function (fn) {
var deferred = $q.defer()
deferred.resolve('Error')
fn(deferred.promise)
}
}
},
token: 'thisisafaketokenfortesting'
}
})
}))
And throws this error:
Expected function not to throw, but it threw TypeError: undefined is not an object (near '...}).error(function () {...').
target/MPMReportGenerator-1.0.0/js/app.service.spec.js:42:43
loaded#http://localhost:9876/context.js:151:17
My actual test is this:
it('should get sections', function () {
$httpBackend.expect('GET', '/MPMReportGenerator/api/categories/all').respond(200)
sectionService.getSections()
expect($httpBackend.flush).not.toThrow()
})
I finally figured it out.
Here is the needed code if anyone wants to test an Angular app with keycloak:
beforeEach(
module(function ($provide) {
$provide.factory('Auth', function ($q) {
return {
updateToken: function (minValidity) {
return {
success: function () {
return {
error: function () {
var deferred = $q.defer()
return deferred.promise
}
}
}
}
},
token: 'thisisafaketokenfortesting'
}
})
}))
Note that you will likely need to mock other parts of the keycloak library if you intend to test the interceptors provided in the official examples.
Edit
Don't use the code above, the following works much better:
$provide.factory('Auth', function () {
return {
updateToken: function (minValidity) {
return {
success: function () {
return this
},
error: function () {
return this
}
}
},
token: 'thisisafaketokenfortesting'
}
})

How to mock $compile function when testing a factory with Jasmine

My problem is very similar to this post but just different enough that the accepted answer doesn't work for me. Essentially, I have an Angular factory which makes use of $compile, which returns a promise, and I want to mock it in Jasmine. Here is a simplified version of the factory:
angular.module('app.common')
.factory('myFactory', myFactory);
myFactory.$inject = ['$compile', '$rootScope'];
function myFactory($compile, $rootScope) {
var factory = {
testFunc: testFunc
}
return factory;
function testFunc(stuff) {
angular.element(document.body).append($compile(stuff)($rootScope));
}
}
And here is my test:
describe("Common", function() {
// I have many common services
beforeEach(function() {
angular.mock.module('app.common');
});
describe("Factory Example: ", function() {
var mockCompile, mockRoot, aFactory;
beforeEach(function() {
module(function($provide) {
$provide.value('$compile', jasmine.createSpy('$compile'));
$provide.value('$rootScope', jasmine.createSpy('$rootScope'));
});
});
beforeEach(inject(function($compile, $rootScope, myFactory, $q) {
mockCompile = $compile;
mockCompile = function() {
var deferred = $q.defer();
deferred.resolve('remote call result');
return deferred.promise;
};
mockRoot = $rootScope;
aFactory = myFactory;
}));
it('should work', function() {
aFactory.testFunc('stuff');
expect(true).toBe(true);
});
});
});
This code is complaining at me that $compile does not return a promise. It appears that $provide doesn't know about the new function assignment from inject. Ive been hacking around at it but Im new to Jasmine so I dont really know what Im doing. Any help would be appreciated - and if there's an easier way to do what Im trying to do please let me know!
Okay, hooray I solved it!
For those who may be interested in the future:
The first problem was I was incorrectly assuming that $compile returns a promise, which it does not. It returns a function which takes the scope as an argument. If you do need to mock a function that returns a promise, check out this post which helped me figure out how.
Of course, that didn't work for me because all I needed to do was have my mocked $compile return a function. The following code worked for me:
describe("Common", function() {
// I have many common services
beforeEach(function() {
angular.mock.module('app.common');
});
describe("Factory Example: ", function() {
var mockCompile, mockRoot, aFactory;
beforeEach(function() {
module(function($provide) {
// Just use callFake to have $compile return a function
$provide.value('$compile', jasmine.createSpy().and.callFake(function() {
return function(scope) { scope(); };
});
$provide.value('$rootScope', jasmine.createSpy());
});
});
beforeEach(inject(function($compile, $rootScope, myFactory, $q) {
mockCompile = $compile;
mockRoot = $rootScope;
aFactory = myFactory;
}));
it('should work', function() {
aFactory.testFunc('stuff');
expect(mockCompile).toHaveBeenCalled();
expect(mockRoot).toHaveBeenCalled();
});
});

Defining custom provider and $injector:unpr Unknow provider

Im trying to understand hows providers works and i make a test based in angularjs documentation and i wrote a simple provider :
(function( window, angular, undefined ){"use strict";
function MyProviderExample(foo)
{
this.testdrive = function()
{
console.log(foo);
}
console.log("init");
}
angular.module('app',[])
.provider('$myProvider',function (){
var foo = "bar";
this.$get = function()
{
return new MyProviderExample(foo);
}
console.log("ey....");
}).config(function($myProvider){
console.log("wut");
$myProvider.foo = "foo";
});
})(window, window.angular);
When i run the code always returns
Uncaught Error: [$injector:modulerr] Failed to instantiate module app due to:
Error: [$injector:unpr] Unknown provider: $myProvider
I was trying to understand what fails but i cant see my mistake, if someone can helps i appreciate
I think you need to remove function($myProvider) from the .config section. Like this:
angular.module('app', [])
.provider('$myProvider', function () {
this.$get = function () {
// --
}
console.log("loaded $myProvider");
})
.config(function(){
console.log("loaded config");
})
.controller('Main',
function main() {
console.log('loaded mycontroller')
});
What are you trying to do with $myProvider.foo = "foo";?

TypeError: parsed is undefined on angularjs service unit test

I am trying to make a unit test for a service that uses $http. I am using Jasmine and I keep on getting this error:
TypeError: parsed is undefined in angular.js (line 13737)
This is what my service looks like:
angular.module('myapp.services', [])
.factory('inviteService', ['$rootScope', '$http', function($rootScope, $http) {
var inviteService = {
token: '',
getInvite: function(callback, errorCallback) {
$http.get('/invites/' + this.token + '/get-invite')
.success(function(data) {
callback(data);
})
.error(function(data, status, headers, config) {
errorCallback(status);
});
}
};
return inviteService;
}]);
This is what my test looks like:
describe ('Invite Service', function () {
var $httpBackend, inviteService, authRequestHandler;
var token = '1123581321';
beforeEach(module('myapp.services'));
beforeEach(inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
authRequestHandler = $httpBackend.when('/invites/' + token + '/get-invite').respond({userId: 'userX'}, {'A-Token': 'xxx'});
inviteService = $injector.get('inviteService');
}));
afterEach (function () {
$httpBackend.verifyNoOutstandingExpectation ();
$httpBackend.verifyNoOutstandingRequest ();
});
describe ('getInvite', function () {
beforeEach(function () {
inviteService.token = token;
});
it ('should return the invite', function () {
$httpBackend.expectGET('/invites/' + token + '/get-invite');
inviteService.getInvite();
$httpBackend.flush();
});
});
});
I am pretty new to unit testing angularjs based apps and I used the example in the angularjs documentation
https://docs.angularjs.org/api/ngMock/service/$httpBackend
I am not sure what I could be missing, and I already tried different things and I always get the same error, any help will be appreciated.
The parsed variable is the URL from the service in question. It is undefined for one of the following reasons:
URL is malformed
$http.get is not called
token is not defined
sucess and error callbacks have no data
.respond is not called
.respond does not include a response object as an argument
For example:
describe('simple test', test);
function test()
{
it('should call inviteService and pass mock data', foo);
function foo()
{
module('myapp.services');
inject(myServiceTest);
function myServiceTest(inviteService, $httpBackend)
{
$httpBackend.expect('GET', /.*/).respond(200, 'bar');
function callback(){};
inviteService.getInvite.token = '1123581321';
inviteService.getInvite(callback, callback);
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
}
}
}
References
AngularJS source: angular-mocksSpec.js - "should throw exception when only parsed body differs from expected body object"
AngularJS source: urlUtils.js
AngularJS source: urlUtilsSpec.js

How to use $httpBackend.passThrough() with Karma unit tests

I've looked up and down and tried all kinds of things to make the E2E passThrough() actually work. Only examples are for templates. I don't think it actually can work but if someone has ever seen a working example I would love to see it. Basically I want real XHR calls in my Karma tests instead of mocks.
This is where I am at - i've tried a bunch of variations - right now I get an Unknown provider: cartServiceProvider error - sometimes I get that injected but then it's something else.
myAppDev = angular.module('myApp', ['ngMockE2E']);
myAppDev.run(function($httpBackend) {
$httpBackend.whenPOST(/^api\/cart\/save/).passThrough();
});
beforeEach(angular.module('myApp'));
beforeEach(inject(function(_$httpBackend_, _$rootScope_, _$http_e) {
$scope = _$rootScope_;
$http = _$http_;
$httpBackend = _$httpBackend_;
}));
describe('#createOrder()', function() {
it('should return an order ID', function(done) {
inject(function(cartService) {
$scope.$apply(function() {
cartService.createOrder(function(res) {
done();
});
});
})
});
});
Sample service:
angular.module('myApp').factory('cartService', function($http) {
Cart.prototype.createOrder = function(callback) {
var self = this;
var data = {}
$http.post('/api/cart/save', data ).success(function(res) {
self.order = res.id;
callback(res);
});
};

Categories