Home > front end >  How to mock an imported function into a test suite in NestJs?
How to mock an imported function into a test suite in NestJs?

Time:02-24

I want to write a unit test for my payment service but I'm receiving this error:

source.subscribe is not a function
 at ./node_modules/rxjs/src/internal/lastValueFrom.ts:60:12

This is my service

import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
import { lastValueFrom } from 'rxjs';
import { PaymentInfo } from 'src/utils/types/paymentInfo';

@Injectable()
export class PaymentsService {
  constructor(private readonly httpService: HttpService) {}

  private createHeaderWithAuth(auth, contentType = 'application/json') {
    return {
      headers: {
        authorization: auth.replace('Bearer', '').trim(),
        'Content-Type': contentType,
      },
    };
  }

  async makePayment(auth: string, paymentInfo: PaymentInfo) {
    const configs = this.createHeaderWithAuth(auth);
    const response = await lastValueFrom(
      await this.httpService.post(
        `${process.env.PAYMENT_URL}/transaction/pay`,
        paymentInfo,
        configs
      )
    ).catch((error) => {
      console.log(error);
      throw new Error(error.response.data.message);
    });

    return response.data;
  }
}

So with a bit of searching and tinkering found out that this is caused by my import of a rxjs function to resolve the observable setted by axios. I've searched ways to mock this function so I can properly test my service. But none of them gave me a solution, the questions i found only revolved around functions with modules, but these have none since is imported from a third party lib.

This is my test suite:

describe('Payments Service', () => {
  let service: PaymentsService;

  let mockedHttpService = {
    post: jest
      .fn()
      .mockImplementation(
        async (
          url: string,
          paymentInfo: PaymentInfo,
          header = mockedHeader
        ) => {
          return { mockedSuccessfulResponse };
        }
      ),
    get: jest
      .fn()
      .mockImplementation(async (url: string, header = mockedHeader) => {
        return { ...mockedSuccessfulResponse, data: mockedUserCards };
      }),
  };
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        PaymentsService,
        {
          provide: HttpService,
          useValue: mockedHttpService,
        },
      ],
    }).compile();
    service = module.get<PaymentsService>(PaymentsService);
  });

  
  describe('Initialize', () => {
    it('should define service', () => {
      expect(service).toBeDefined();
  });

  describe('makePayment', () => {
    it('should make a payment', async () => {
      const payment = await service.makePayment(mockedAuth, mockedPaymentInfo);
      expect(mockedHttpService.post).toHaveBeenCalledWith(
        `${process.env.PAYMENT_URL}/transaction/pay`,
        mockedPaymentInfo,
        mockedHeader
      );

      expect(payment).toBe(mockedSuccessfulResponse);
    });
  });
  });

Ps.: I removed the mocked objects to reduce the amount of code to read

CodePudding user response:

you should use the of operator from rxjs, and drop the async keyword. Like:

      .mockImplementation(
        (
          url: string,
          paymentInfo: PaymentInfo,
          header = mockedHeader
        ) => {
          return of({ mockedSuccessfulResponse });
        }

otherwise lastValueFrom won't receive an observable object.

  • Related