Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue while (unit test) mocking container.get service #32

Closed
lineldcosta opened this issue Aug 24, 2019 · 2 comments
Closed

Issue while (unit test) mocking container.get service #32

lineldcosta opened this issue Aug 24, 2019 · 2 comments
Labels
question Further information is requested

Comments

@lineldcosta
Copy link

lineldcosta commented Aug 24, 2019

Hello santiq,

I am back again:-)!. Hey i am having issues in unit testing container.get(service), checkout the below code.

import * as _ from 'lodash';
import {Service, Inject, Container} from 'typedi';
import OTPService from '../otp/index';

@Service()
export default class DevicechangeService {
  constructor(@Inject('devicechangeModel')private devicechangeModel, @Inject('otpModel')private otpModel, @Inject('logger')private logger,) {}

  /**
   *
   * Service to change the device
   *
   * @method deviceChange
   * @param {number} accountId
   * @param {string} deviceId
   * @param {number} mobile
   * @return {object} resolves after sending otp
   */

  public async deviceChange(accountId : number, deviceId : string, newdeviceId : string, created : number, mobile : number,) : Promise < any > {
    try {
      await this
        .devicechangeModel
        .addDeviceChangeDetails(accountId);

       let OTPServiceInstance = Container.get(OTPService);


      await this
        .otpModel
        .updateOTPtoInactiveForModule('DEVICE_CHANGE', accountId);
      //get the new otp

        //get the user details
        //send the otp
        let result = await OTPServiceInstance.sendOTP(serviceprovider, mobile, otpMessage, isd);
        if (result instanceof Object) {
          return result;
        }
        return true;
      } else {
        return {
          field: 421,
          message: errorCode('account').getMessage(421)
        };
      }
    } catch (e) {
      throw e;
    }
  }
}

Here i am not able to set mock class for OTPService, so not able to alter container.get(OTPService) from the above code.

I have tried below but still it does'nt come inside mocked service.

@Service()
class OTPService {
  public generateOTP : any = jest
    .fn()
    .mockImplementation(async(accountId : number, deviceId : string, status : string) => new Promise((resolve : any) => {
      console.log('calls here')
      resolve({})
    }),);
  public sendOTP : any = jest
    .fn()
    .mockImplementation(async(serviceprovider : string, mobile : number, otpMessage : string, isd : number) => new Promise((resolve : any) => resolve({})),);
}

describe('Account', () => {
  beforeEach(done => {
    Container.set(OTPService, new OTPService());
    done();
  });
  describe('#deviceChange', () => {
    it('check everyting cool', async done => {
      let logger = {};
      let otpModel = {
        updateOTPtoInactiveForModule: jest
          .fn()
          .mockImplementation(async(deviceChange : string, accountId : number) => new Promise((resolve : any) => resolve(true))),
        getUserPhoneService: jest
          .fn()
          .mockResolvedValue(true)
      };
      let devicechangeModel = {
        addDeviceChangeDetails: jest
          .fn()
          .mockResolvedValue(true),
        updateDeviceChangeToInactive: jest
          .fn()
          .mockResolvedValue(true)
      };
      // Container.set('OTPService', new OTPService());
      let DeviceChangeServiceInstance = new DeviceChangeService(devicechangeModel, otpModel, logger);
      let deviceChangeStatus = DeviceChangeServiceInstance.deviceChange(accountId, deviceId, newdeviceId, created, mobile);
    });
  });
});

Guys any idea how to solve this...

@lineldcosta lineldcosta changed the title Issue while mocking container.get service Issue while (unit test) mocking container.get service Aug 24, 2019
@santiq
Copy link
Owner

santiq commented Aug 24, 2019

In the unit test code, you don't have to use Container nor the @service or @Inject decorators.

The idea is that you mock every dependency of the class that you are going to test.

Also, you don't necessarily need to write a Class for the mock, just use a normal object with the involved functions mocked with jest

describe('Account', () => {
  describe('#deviceChange', () => {
    it('check everyting cool', async done => {
      let logger = {};
      let otpModel = {
        updateOTPtoInactiveForModule: jest
          .fn()
          .mockImplementation(async(deviceChange : string, accountId : number) => new Promise((resolve : any) => resolve(true))),
        getUserPhoneService: jest
          .fn()
          .mockResolvedValue(true)
      };
      let devicechangeModel = {
        addDeviceChangeDetails: jest
          .fn()
          .mockResolvedValue(true),
        updateDeviceChangeToInactive: jest
          .fn()
          .mockResolvedValue(true)
      };
      let DeviceChangeServiceInstance = new DeviceChangeService(devicechangeModel, otpModel, logger);
      let deviceChangeStatus = DeviceChangeServiceInstance.deviceChange(accountId, deviceId, newdeviceId, created, mobile);
    });
  });
});

Now the problem that I see in your code is that you are using the service locator to obtain a dependency that should be injected into the constructor.

So, what I'm saying is that you need to change this

 let OTPServiceInstance = Container.get(OTPService);

For this

  constructor(
    @Inject('devicechangeModel')private devicechangeModel, 
    @Inject('otpModel')private otpModel,
    @Inject('logger')private logger,
    @Inject() private OTPServiceInstance: OTPService, // Here is injected.
) {
}

The function Container.get (formal name 'Service Locator') should only be used in the entry point of the code execution
In this project, that is at top-level code in the API Route Controller.
Also in the event handlers, because the execution is detached from the API controller context

What do you think?

@santiq santiq added the question Further information is requested label Aug 24, 2019
@lineldcosta
Copy link
Author

Hello santiq,

Sorry for the delay, but as per ur instructions, i implemented it and works perfectly, and checked out other git repo like many have implemented the same way you have guided.

Thanks satiq,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants