NG6-starter
NG6-starter copied to clipboard
Unit Test using Karma for the controller with Service
Thank you for the great material. But I have a question for the unit test.
I have created one angularjs service and tried to inject in my teams.spec.js for unit test. But I get the error like this. Error: [$injector:unpr] unknown provider : EnvServiceProvider <- EnvService ...
Here is the source code.
import TeamsModule from './teams'
import TeamsController from './teams.controller';
import TeamsComponent from './teams.component';
import TeamsTemplate from './teams.html';
import Services from '../../services/services.js'
import 'ngstorage';
import env from '../../services/env.js'
describe('Teams', () => {
let $rootScope, makeController, $localStorage, EnvService, $http;
beforeEach(window.module(TeamsModule.name));
beforeEach(window.module('ngStorage'));
beforeEach(window.module(Services.name));
// come out the error when injecting
beforeEach(inject((_$rootScope_, _$http_, _$localStorage_, _EnvService_) => {
$rootScope = _$rootScope_;
$http = _$http_;
EnvService = _EnvService_;
makeController = () => {
return new TeamsController();
};
}));
describe('Module', () => {
// top-level specs: i.e., routes, injection, naming
});
describe('Controller', () => {
// controller specs
it('has a name property [REMOVE]', () => { // erase if removing this.name from the controller
let controller = makeController();
expect(controller).to.have.property('name');
});
});
describe('Template', () => {
// template specs
// tip: use regex to ensure correct bindings are used e.g., {{ }}
it('has name in template [REMOVE]', () => {
// expect(TeamsTemplate).to.match(/{{\s?vm\.name\s?}}/g);
});
});
describe('Component', () => {
// component/directive specs
let component = TeamsComponent;
it('includes the intended template',() => {
expect(component.template).to.equal(TeamsTemplate);
});
it('uses `controllerAs` syntax', () => {
expect(component).to.have.property('controllerAs');
});
it('invokes the right controller', () => {
expect(component.controller).to.equal(TeamsController);
});
});
});
//---------- ...\client\app\services\services.js ----------
import angular from 'angular';
import uiRouter from 'angular-ui-router';
import 'ngstorage';
import env from './env';
import security from './security';
import auth from './auth';
import events from './events';
import teams from './teams';
import companies from './companies';
import profile from './profile';
import common from './common';
export default angular
.module('app.services', [])
.service({
env,
security,
auth,
events,
teams,
companies,
profile,
common,
})
---------- ...\client\app\services\env.js ----------
import angular from 'angular';
import uiRouter from 'angular-ui-router';
import 'ngstorage';
export default class EnvService {
constructor($http, $localStorage, $state, $q) {
this.$http = $http;
this.$localStorage = $localStorage;
this.$state = $state;
this.$q = $q;
}
getAPIEndPoint() {
// return 'https://dev.int.jobbleapp.com/jobbleapi/';
return 'https://jbl-qa-api.int.jobbleapp.com/jobbleapi/';
}
getTransActionFee() {
return 0.05;
}
}
EnvService.$inject = ['$http', '$localStorage', '$state', '$q'];
Could you let me know what the problem is?
I am really appreciate if you provide a unit test example with injecting a custom angular service.
Thanks
I came across the same issue. You need to include it in the makeController
function, like this:
makeController = () => {
return new myController(myService);
};
Just use the component controller for unit tests. I have updated this repo as well. It will make things like this pretty easy
@samithaf ? The code above is using the component controller. Also, I don't understand what you have updated, there's no recent commits from you.
@ttbarnes I can't see above code is using component controller. I have updated this repo to use component controller recetly. Here is my commit, https://github.com/AngularClass/NG6-starter/commit/1e861070db71ceb6a0ceffa73353ba24e15cfaec
@samithaf Sorry if i'm misunderstanding something, but I can't see how this is related. This issue is about injecting a service into a controller, and unit testing it.
In fact, I can't see an example of a service in this repo, nor an example of the above issue. I can do a PR for these things.
why are you injecting the service into your spec for the teamModule?? Ive created a simple service (for firebase initial config) that I have a separate spec for, my modules that use it have it injected into the controller component but I don't have it in the spec for that component.
Oh and what @samithaf is referring to I think is that his commit to use component controllers simplifies the spec quite a bit.
@pziegler @ttbarnes yup. as per my original comment, i was suggesting to use Angular component controller for unit tests. Component controller facilitate to write unit tests with ease! Also you can mock the entire service easily as follows.
describe('Controller', () => {
beforeEach(() => {
// mock login service
const mockLoginService = () => {
const service = {
login: (form) => {
const deferred = $q.defer();
if (form.username === 'david') {
deferred.resolve();
} else {
deferred.reject({ status: 403, reason: 'invalid user' });
}
return deferred.promise;
},
invalidateSession: () => {},
};
return service;
};
// mock user service
const mockUserService = () => {
const service = {
fetchProfile: () => {
const deferred = $q.defer();
deferred.resolve();
return deferred.promise;
},
};
return service;
};
// use $componentController helper to init the controller
// https://docs.angularjs.org/api/ngMock/service/$componentController
controller = $componentController('login', {
$q,
$scope: $rootScope.$new(),
loginService: mockLoginService(),
userService: mockUserService(),
});
});
@pziegler @samithaf Yes, the confusion has been that @calemyers723 wasn't sure how to inject a service into a unit test. Mocking a service is great, but you can also inject and mock, for example some service methods.
I think it's worth putting in an example of injecting a service, with or without with mocking.