如何在 NestJs 中将导入的函数模拟到测试套件中?

发布于 2025-01-09 09:54:51 字数 2755 浏览 1 评论 0原文

我想为我的支付服务编写一个单元测试,但收到此错误:

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

这是我的服务

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;
  }
}

因此,经过一些搜索和修补发现这是由我导入 rxjs 函数来解析由设置的可观察值引起的轴。 我已经搜索了模拟此功能的方法,以便我可以正确测试我的服务。但他们都没有给我一个解决方案,我发现的问题只围绕带有模块的函数,但这些问题没有,因为是从第三方库导入的。

这是我的测试套件:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

巨坚强 2025-01-16 09:54:51

您应该使用 rxjs 中的 of 运算符,然后删除 async 关键字。就像:

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

否则 lastValueFrom 将不会收到可观察对象。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文