NG6-starter icon indicating copy to clipboard operation
NG6-starter copied to clipboard

Unit Test using Karma for the controller with Service

Open kyleevert opened this issue 8 years ago • 9 comments

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

kyleevert avatar May 30 '16 19:05 kyleevert

I came across the same issue. You need to include it in the makeController function, like this:

makeController = () => {
  return new myController(myService);
};

ttbarnes avatar Jul 21 '16 18:07 ttbarnes

Just use the component controller for unit tests. I have updated this repo as well. It will make things like this pretty easy

samithaf avatar Jul 25 '16 06:07 samithaf

@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 avatar Jul 25 '16 07:07 ttbarnes

@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 avatar Jul 27 '16 04:07 samithaf

@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.

ttbarnes avatar Jul 27 '16 08:07 ttbarnes

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.

pkishino avatar Jul 27 '16 17:07 pkishino

Oh and what @samithaf is referring to I think is that his commit to use component controllers simplifies the spec quite a bit.

pkishino avatar Jul 27 '16 17:07 pkishino

@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(),
      });
    });

samithaf avatar Jul 28 '16 01:07 samithaf

@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.

ttbarnes avatar Jul 28 '16 08:07 ttbarnes