如何使用 jest 测试 HttpService.Post 调用

问题描述 投票:0回答:5

我正在调用 Nestjs 服务中的 API,如下所示,

import { HttpService, Post } from '@nestjs/common';

export class MyService {

    constructor(private httpClient: HttpService) {}

    public myMethod(input: any) {
        return this.httpClient
            .post<any>(
                this.someUrl,
                this.createObject(input.code),
                { headers: this.createHeader() },
            )
            .pipe(map(response => response.data));
    }
}

如何在不调用实际 API 的情况下模拟/监视对 this.httpClient.post() 的调用以返回响应?

describe('myMethod', () => {
    it('should return the value', async () => {
        const input = {
            code: 'value',
        };
        const result = ['test'];
      
        // spyOn?

        expect(await myService.myMethod(input)).toBe(result);
    });
});
typescript jestjs nestjs ts-jest
5个回答
25
投票

使用spyOn让它工作。

describe('myMethod', () => {
    it('should return the value', async () => {
      const input = {
        code: 'mock value',
      };

      const data = ['test'];

      const response: AxiosResponse<any> = {
        data,
        headers: {},
        config: { url: 'http://localhost:3000/mockUrl' },
        status: 200,
        statusText: 'OK',
      };

      jest
        .spyOn(httpService, 'post')
        .mockImplementationOnce(() => of(response));

      myService.myMethod(input).subscribe(res => {
        expect(res).toEqual(data);
      });
  });
});

13
投票

模拟 http 服务的一个很好的替代方法是在providers数组中声明它,如下所示。

let httpClient: HttpService;
beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        {
          provide: HttpService,
          useValue: {
            post: jest.fn(() => of({
              // your response body goes here 
            })),
          },
        },
      ],
    }).compile();

    httpClient = module.get<HttpService>(HttpService);
  });

通过在测试模块中提供 HttpService 而不是使用间谍,可以确保 HttpModule 不会被导入或使用,并减少测试代码对其他服务的依赖。


1
投票

我有一个方法正在使用我的模拟后调用的结果,所以我最终得到了这个

describe('my test', function () {
  let service: LegalTextAdminClientFactory;

  const httpService = {
    get: jest.fn(),
    post: jest.fn().mockImplementation(() => of({ data: {} })),
  };
  const configService = {
    get: jest.fn().mockReturnValue('mock'),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        { provide: HttpService, useValue: httpService },
        { provide: ConfigService, useValue: configService },
        LegalTextAdminClientFactory,
      ],
    }).compile();

    service = await module.get(LegalTextAdminClientFactory);
  });
});

所以通过返回这个“of()”你甚至可以通过管道传输它的结果


1
投票

正如@Diaboloxx提到的,你应该通过测试中的

HttpService
设置来模拟你的
providers

理想情况下,您应该提供并模拟您依赖注入到构造函数中的任何内容。这样您的测试就可以与给定的文件隔离,在这种情况下可以帮助防止发出真正的请求。

我喜欢使用库中的

mockDeep
jest-mock-extended
来处理对提供者的模拟,以查看对它们调用了什么,以及模拟返回值。这也很好,因为
mockDeep
将强制类型安全来验证您是否正在模拟有效的返回数据。

旁注:测试您的依赖项是否按照您期望的方式调用是一个很好的做法。

import { HttpService } from '@nestjs/axios'
import { Test, TestingModule } from '@nestjs/testing'
import { mockDeep } from 'jest-mock-extended'
import { of } from 'rxjs'
import { AxiosResponse } from 'axios'
import { MyService } from '@/services/my.service'

describe('MyService', () => {
  let myService: MyService

  const httpService = mockDeep<HttpService>()

  beforeEach(async () => {
    const app: TestingModule = await Test.createTestingModule({
      controllers: [myService],
      providers: [
        {
          provide: HttpService,
          useValue: httpService,
        },
      ],
    }).compile()

    myService = app.get<MyService>(MyService)
  })

  describe('#myMethod', () => {
    const response: AxiosResponse<unknown, any> = {
      data: { hello: 'world' },
      headers: {},
      config: { url: 'http://localhost:3000/mockUrl' },
      status: 200,
      statusText: 'OK',
    }

    beforeEach(() => {
      httpService.post.mockReturnValue(of(response))
    })

    it('should return "Hello World!"', async () => {
      const result = await myService.myMethod({ code: 'value' })

      expect(result).toEqual({ hello: 'world' })
    })

    it('calls httpService.post with the correct params', async () => {
      await myService.myMethod({ code: 'value' })

      expect(httpService.post).toHaveBeenLastCalledWith(
        'someBaseUrl/param1/param2',
        { body: 'body' },
        expect.objectContaining({
          headers: {
            header: 'header',
          },
        }),
      )
    })
  })
})

0
投票

如果您使用的是axios

确保在 packakge.json 添加

"moduleNameMapper": {
      "^@shared/shared(|/.*)$": "<rootDir>/libs/shared/src/$1",
      "^@app/shared(|/.*)$": "<rootDir>/libs/shared/src/$1",
      "^axios$": "<rootDir>/node_modules/axios/dist/axios.min.js"
}

© www.soinside.com 2019 - 2024. All rights reserved.