Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write unit test case in loopback 4

I am new to loopback 4. I wish to write unit test cases for my project. However, I could't found proper example where they have used mocking service and using stubs.

Please assist me or share any example of properly written unit test(using mock service and stubs).

Thank You

Below is my code base:

Controller

export class CaptureController {
  constructor(
    @inject(RestBindings.Http.RESPONSE) private response: Response,
    @service(HelperService) private helperService: HelperService,
  ) { }
@post('/capture', {
    'x-controller-name': CaptureSwagger.controller,
    summary: CaptureSwagger.summary,
    description: CaptureSwagger.description,
    responses: {
      '200': CaptureSuccess.response,
      '400': CommonHttpErrors(Messages.badRequest, 400, ''),
      '401': CommonHttpErrors(
        Messages.unauthorized,
        401,
        Messages.unauthorized,
      ),
    },
  })
  async capture(
    @requestBody() body: Capture,
  ): Promise<Response> {
    const response = await this.helperService
      .getGatewayService()
      .capture(body);
    const data = this.helperService.getGatewayKeys(
      response,
      TransactionType.COMPLETE,
    );

    const buildResponse = BuildResponse.prepare({data});
    return this.response.json(buildResponse);
  }
}

HelperService

@injectable({scope: BindingScope.TRANSIENT})
export class HelperService {
  constructor(
    @inject(RestBindings.Http.CONTEXT) private context: RequestContext,
    @service(ConvergeService) private convergeService: ConvergeService,  
   ) { }
getGatewayService() {
    const merchantGateway: MerchantGateway =
      this.context.getSync('merchantGateway');
    const gatewayName: PaymentGateway | string = merchantGateway?.sysName
      ? merchantGateway.sysName
      : '';
    switch (gatewayName.toLowerCase()) {
      case PaymentGateway.CONVERGE:
        return this.convergeService;
      default:
        return this.nexioService;
    }
  }
}

ConvergeService

@injectable({scope: BindingScope.TRANSIENT})
export class ConvergeService {
/**
   * Capture
   * @param body Capture payment
   */
  public capture = async (body: Capture) => {
    const startAt = process.hrtime();
    let errorResp = '';
    const captureXml = `${this.xmlData}<ssl_transaction_type>cccomplete</ssl_transaction_type>
    <ssl_amount>${body.data.amount}</ssl_amount>
    <ssl_txn_id>${body.id}</ssl_txn_id>
    </txn>`;
    try {
      const resp: {data: string} = await axiosApi.post(
        `${this.convergeObj.apiUrl}`,
        captureXml,
      );
      const jsonResp = this.parseXmlToJson(resp.data);

      if (jsonResp?.txn?.errorCode) {
        errorResp = resp.data;
        throw new ResponseError.BadRequest(jsonResp.txn.errorMessage);
      }
      return jsonResp.txn ? jsonResp.txn : jsonResp;
    } catch (err) {
      this.commonService.getErrorResponse(err);
    }
  };
}
like image 907
Harshit Rajput Avatar asked Oct 15 '25 04:10

Harshit Rajput


1 Answers

i will share a sample. You can customize it for your services, i think. We assume, we have a car service and simple price calculator function like this:

@injectable({scope: BindingScope.TRANSIENT})
export class CarService {
constructor(
  @repository(CarRepository)
  private carRepository: CarRepository,
) {}

  async calculateCarPricesWithTaxes(tax: number): Promise<[]> {
    const cars = await this.carRepository.find({where: {price: {gte: 10000}}});

    return cars.map(car => (car.price * tax / 100) + car.price);
  }
}

This service's unit test class like this:

describe('CarService', () => {  

  let carRepository: StubbedInstanceWithSinonAccessor<CarRepository>;

  beforeEach(() => {
    carRepository = createStubInstance(CarRepository);

    carService = new CarService(carRepository);
  });

  after(() => {
    sinon.restore();
  });

  describe('calculateCarPricesWithTaxes', () => {
    it('should get new prices for cars', async () => {
      const find = carRepository.stubs.find;
      find.resolves([
        {id: 1, price: 10000},
        {id: 1, price: 20000},
      ]);

      const res = await carService.calculateCarPricesWithTaxes(10);

      expect(res).to.eql([11000, 22000]);
      // expect(res).to.not.eql([110, 220]);

      sinon.assert.calledWith(find, {where: {price: {gte: 10000}}});

      // sinon.assert.calledOnce(find);
    });
  });
});

i hope this is helpful

like image 77
akadirdev Avatar answered Oct 16 '25 16:10

akadirdev