Karma unknown provider error - angularjs

I'm a total noob to unit testing javaScript, angularJS, and Karma. I've been able to write passing tests for the controllers, but now that I'm attempting to test the services, I'm getting the following error: Unknown provider <- nProvider <- User where User is the name of my service and from what I can tell, "n" is the $http dependency for that service (minified). Here is my UserService (TypeScript):
/// <reference path="../../lib/angular/angular.d.ts" />
module MyApp.Services{
export class UserService{
private _apiBase: string = "api/";
constructor(private $http: ng.IHttpService, private $q: ng.IQService) {
}
getUser(userSearchParams: string): ng.IPromise{
var deferred = this.$q.defer();
this.$http.get(this._apiBase + "Users/" + userSearchParams).success((data) => {
deferred.resolve(data);
}).error(() => {
deferred.reject();
});
return deferred.promise;
}
}
}
and here is my generic Services.ts file:
/// <reference path="../_all.d.ts" />
'use strict'
var mod = angular.module('MyApp.Services', []);
mod.factory('User', [<any>"$http", <any>"$q", function ($http, $q) {
return new MyApp.Services.UserService($http, $q);
}]);
and finally, my test script:
'use strict';
describe('UserServiceTests', function () {
beforeEach(module('main')); //this is my main angular module - set up in app.ts
it('should issue a GET request to api/Users/whatever when the search parameter is whatever', inject(function(User) {
expect(2).toBe(2);
}));
})
I've created another test app with the same structure and I don't get the error seen above. I don't know what I'm missing. Let me know if there is anything more that I need to post in order to find the problem. Any help is appreciated.
Edit: After much searching and reading, it appears that this is related to dependency injection here and here. Upon minification, the parameter names get changed, thus causing the error. As proof, if I delete the min files and re-run the tests, they pass.
The $inject function can be used to fix this, but I don't see how to do it when the controller is created as a class using TypeScript. I'm not sure if I can use the resolve property of $routeProvider.when.

Ok, I found this post, which allowed me to resolve the issue. By adding an "inject" property to my TypeScript classes (with the appropriate values in the array), Angular knows how to handle the DI. The only drawback I see is the potential for error in the order the dependencies are listed, or omission in the event a new dependency is added at a later time.

Related

How to use module function inside another factory in angularjs

I have configurations.js file which having
var configurationModule = angular.module('configuration', ['restangular', 'notification']);
configurationModule.factory('clientConfigSvc', function (notificationMgr, $interpolate, $injector) {
function getConfig(configKey) {
return getNestedPropertiesByString(activeEnvironment, configKey);
}
}
and having another javascript file with below code
angular.module('ds.coupons').factory('CouponsREST', ['Restangular', 'SiteConfigSvc', 'settings',function (Restangular, siteConfig, settings, configurationModule) {
configSvc.getConfig('services.baseUrl'); /// need to call this function
}
Actually i want function configSvc.getConfig inside second angular factory
Yes After tired of trying lots of solution, today morning working on moving train got idea to fix, dont know if its correct way but its working. but still looking for correct fix if mine was not correct as per angularjs practise.
Here is my solution.
1. I added configuration module in app.js alogn with other
window.app = angular.module('ds.app', [
'restangular',
'ui.select',
'ui.router',
'ds.shared',
'ds.utils',
'ds.i18n',
'configuration']);
I added clientConfigSVC in my factory where i want to use configuration function
angular.module('ds.coupons')
.factory('CouponsREST', ['Restangular', 'SiteConfigSvc','settings', 'clientConfigSvc',
function(Restangular, siteConfig, settings, clientConfig) { }
and then inside CouponsREST factory with function clientConfig.getConfig() i am getting value

unit testing angular service, beforeEach inject doesn't execute

I'm trying to write unit tests for an angular service with jasmine/karma. I have a similar service test, which works just fine. But this one has some additional dependencies, is in a different module and just doesn't find the service with the inject.
The service looks something like this. bService is in the same module, but commonFactory and commonService are in another module, say commonModule.
(function () {
'use strict';
angular
.module('myService')
.service('aService', aService);
aService.$inject = [
'commonFactory',
'commonService'
'bService'
];
function aService (
commonFactory,
commonService,
bService
) {
};
return {
codeIWantToTest: CodeIWantToTest;
}
function CodeIWantToTest () {
console.log('surprise!');
}
})();
My jasmine test looks like:
describe('myService.aService', function () {
'use strict';
var aService;
// I tried adding beforeEach(module('commonModule')); too, but that didn't do anything
beforeEach(module('myService'));
beforeEach(function() {
inject(function(_aService_) {
console.log('getting aService');
aService = _aService_;
});
});
it('tests my service is defined', function() {
expect(myService).toBeDefined();
});
});
This test fails. myService isn't defined and the console.log in the inject function doesn't ever fire. My karma.conf.js basically lists the dependencies in the order that they're injected into the service, then adds the service then the test.
What would cause the inject to not grab the service? What am I missing? I mentioned I have a similar test for commonService and it works just fine. So I'm baffled.
Another dev on my team found the solution and I wanted to post it as an answer for the future people. I had a feeling it was a dependency problem, and it was. While we were loading all of the JS stuff correctly, the template that the component uses was loading another js dependency. So to fix this for jasmine, we had two different solutions:
at the top of the component test file, we could add:
beforeEach(function () {
module(function ($provide) {
$provide.constant('myMissingDependency', {
// do things to load the dependency here
});
});
});
In our case it was a translation library
The other solution was to add a 'shim' file into the unit test directory and load it with karma.config.js ahead of the tests. That looked like:
(function() {
angular
.module('MyService')
.constant('myMissingDependency', Object.freeze({
// things to load the dependency
}));
})();
I wasn't able to switch to Chrome because we're using Docker and I couldn't get the tests to run locally to run Chrome. So adding a second set of eyes to this was what I needed.

Error: [$injector:unpr] Unknown provider: $q. Confused about DI syntax

Snippet 1 does not work. I get Error: [$injector:unpr] Unknown provider: $q
/* Snippet 1*/
var mApp = angular.module('MyApp',[]);
mApp.provider('authProvider', ['$q', function($q) {
this.$get = function(){
authProvider = {};
authProvider.Title = function(){
var deferred = $q.defer();
deferred.resolve("promise resolved");
return deferred.promise;
}
return authProvider;
}
}]);
However, Snippet 2 works. I am confused why that is ? All the factory sample codes i have read, inject the dependency in the first line such as
.factory('MyFactory',[$q,function($q) {}]);
Why doesnt that style work in the provider code above ? Also, why are we injecting $q below in the GET declaration but not further down in the TITLE declaration.
/* Snippet 2*/
mApp.provider('authProvider', function() {
this.$get = function($q){
authProvider = {};
authProvider.Title = function(){
var deferred = $q.defer();
deferred.resolve("promise resolved");
return deferred.promise;
}
return authProvider;
}
});
Please help!!!
(The code doesn't do anything right now. I am just trying to learn syntax)
you can't do direct DI in provider , when you are using provider you have to inject your component in $get .
Reason you cannot inject dependency into the provider directly is that the provider runs during the module loading phase whereas the $get is run when instantiating the service you are providing.
You can not use any service during the loading/configuration phase of your modules.
All the factory sample codes i have read, inject the dependency in the first line such as .factory('MyFactory',[$q,function($q) {}]); Why doesn't that style work in the provider code above ?
This graphic has always helped me understand the difference between a provider and a factory:
source: simplygoodcode.com
The factory function is the $get function of the provider. Before injection, the provider constuction function can configure the $get function. The $get function is where injection happens.
The provider construction function isn't injectable. That's why you get that error. The $get function is injectable and that is the function that you specify with factory. factory is just syntactic sugar for creating a provider with an empty constructor function.
See Also
Confused about Service vs Factory — this answer

Controller testing fails due to the service dependency injection

When making use of a service in a controller test do you need to initialize the service in the same way you would the controller? By this I mean do you need to pass it its own dependencies?
For example I can initialize my controller like so:
// Instantiate the controller
searchController = $controller( 'VisibilitySearchController',{
$scope: scope,
dataService: dataService
});
}));
so do I need to initialize the service according to the components it needs like $http, $resource etc as well as make spyOn calls on its functions? Or is this/should this be sufficient? (Note - my tests fail when I do the following )
// Instantiate the dataService
dataService = $injector.get( 'dataService' );
it throws this error:
* Error: [$injector:unpr] Unknown provider: $resourceProvider <- $resource <- dataService
The relevant part of the service:
myAppServices.factory('dataService', ['$http', '$resource', 'authService', 'messageService', function ($http, $resource, authService, messageService) {
}
Side note
Note - we are using Maven as our build tool and only make use of Jasmine at this point - trying to bring Karma in as our test-runner as a Maven plugin.
You must provide all the dependencies but you can mock them. This can be done by jasmine like this for example:
var mockedDataService = jasmine.createSpyObj('dataService', ['getData', 'getOtherData']);
And then you inject this mocked service to $provider:
beforeEach(function () {
module(function ($provide) {
$provide.value('dataService', mockedDataService );
});
}
Instance of this mocked service can be retrieved like this then:
inject(function (dataService) {
var dataServiceInstance = dataService;
});
This will provider mocked dataService anytime it is needed. However if you need fully functional dataService you must instantiate it but always you can mock any of its dependecies.
While you can inject dependencies into the controller manually you don't need to do it as long as you have loaded the module the service belongs to.
In your case it looks like you have not loaded the ngResource module.
If you add beforeEach(module('ngResource')) to your test (and make sure the actual script file it lives in is included in Jasmine's fileset) you should not need to inject it manually.
Note that you do not need to load angular core services like $http, but since $resource is not part of core it needs to be loaded like this.
Injecting dependencies manually is mostly useful if you want to provide a mock implementation.

How to get a service from angular on initialization?

I have been looking for a way to get services on initialization of my angular-js application, but could not find how to get it to work. In my case I want to get the $location service to observe the url.
Looking around, I found the services can be retrieved from the injector. To get the injector, I bootstrapped my application like this:
var angularApp = angular.module("MyApp", []);
var angularInjector = angular.injector(["MyApp", "ng"]);
angularApp.run(initializeAngularApp);
initializeAngularApp()
{
var location = angularInjector.get("$location");
}
This throws an Error:
Unknown provider: $rootElementProvider <- $rootElement <- $location
My understanding is that initializeAngularApp() should get called once the injector is done initializing. But judging from the error I get, it would not be the case.
What is the best way to get the services from the injector when my application initializes?
I found my answer and I did not need to instantiate the injector myself to get the service.
Services are injectable in the run() function, so doing:
angularApp.run(intializeAngularApp);
with
initializeAngularApp($rootScope, $location)
{
$rootScope.location = $location;
$rootScope.$watch("location.url()", function () { alert("url changed"); });
}
works.

Resources