首页 > 解决方案 > Nestjs中的单元测试ClientProxy

问题描述

在 Nestjs 中,我尝试模拟 ClientProxy,但我收到一个输入错误,告诉我需要定义 ClientProxy 类的所有属性。当然,我不想模拟这个类的每个属性。关于如何解决这个问题的任何想法?

这是一段简化的代码:

服务.ts

@Injectable()
export class SimulationsService {
  constructor() {}
  @Client({
    transport: Transport.NATS,
    options: {
      url: env.nats,
      user: env.usernats,
      pass: env.passnats,
    },
  })
  simulationClient: ClientProxy;

  method1(){
    this.simulationClient.send('simulatePattern', simulation)
    .pipe(
      map(sim=>{
        //SOME CODE
        return sim;
      })
    });
  }

服务规范.ts

describe('method1', () => {
  it('should return a processed Simulation', async () => {
    jest.spyOn(
        service,
        'simulationClient',
        'get',
      ).mockImplementation(()=>{
        return {send() {
          return of(simulationMock as Simulation);
        }}
      });
    });
    expect(await service.update('test', unfedSimulationMock)).toEqual(simulationMock);
  });
});

错误输出:

类型 '{ send(): Observable; }' 缺少“ClientProxy”类型的以下属性:connect、close、routingMap、emit 等 7 个。

标签: unit-testingmockingjestjsnestjs

解决方案


这是一个解决方案:

export interface ClientProxy {
  send(pattern: string, simulation: any): any;
  connect(): any;
  close(): any;
  routingMap(): any;
}

我简化了以下代码service.ts

// tslint:disable-next-line: interface-name
export interface ClientProxy {
  send(pattern: string, simulation: any): any;
  connect(): any;
  close(): any;
  routingMap(): any;
}

export class SimulationsService {
  constructor(private simulationClient: ClientProxy) {}

  public method1() {
    const simulation = {};
    this.simulationClient.send('simulatePattern', simulation);
  }
}

单元测试,部分模拟ClientProxy,这意味着我们可以模拟特定方法而不会出现类型错误。

import { SimulationsService, ClientProxy } from './service';
import { mock } from '../../__utils';

const simulationClientMock = mock<ClientProxy>('send');

// Argument of type '{ send: Mock<any, any>; }' is not assignable to parameter of type 'ClientProxy'.
// Type '{ send: Mock<any, any>; }' is missing the following properties from type 'ClientProxy': connect, close, routingMap
// const simulationClientMock = {
//   send: jest.fn()
// };

const service = new SimulationsService(simulationClientMock);

describe('SimulationsService', () => {
  describe('#method1', () => {
    it('t1', () => {
      simulationClientMock.send.mockReturnValueOnce('mocked data');
      service.method1();
      expect(simulationClientMock.send).toBeCalledWith('simulatePattern', {});
    });
  });
});

__util.ts

type GenericFunction = (...args: any[]) => any;

type PickByTypeKeyFilter<T, C> = {
  [K in keyof T]: T[K] extends C ? K : never;
};

type KeysByType<T, C> = PickByTypeKeyFilter<T, C>[keyof T];

type ValuesByType<T, C> = {
  [K in keyof T]: T[K] extends C ? T[K] : never;
};

type PickByType<T, C> = Pick<ValuesByType<T, C>, KeysByType<T, C>>;

type MethodsOf<T> = KeysByType<Required<T>, GenericFunction>;

type InterfaceOf<T> = PickByType<T, GenericFunction>;

type PartiallyMockedInterfaceOf<T> = {
  [K in MethodsOf<T>]?: jest.Mock<InterfaceOf<T>[K]>;
};

export function mock<T>(...mockedMethods: Array<MethodsOf<T>>): jest.Mocked<T> {
  const partiallyMocked: PartiallyMockedInterfaceOf<T> = {};
  mockedMethods.forEach(mockedMethod => (partiallyMocked[mockedMethod] = jest.fn()));
  return partiallyMocked as jest.Mocked<T>;
}

覆盖率 100% 的单元测试结果:

 PASS  src/stackoverflow/57960039/service.spec.ts
  SimulationsService
    #method1
      ✓ t1 (4ms)

----------------------------|----------|----------|----------|----------|-------------------|
File                        |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------------------------|----------|----------|----------|----------|-------------------|
All files                   |      100 |      100 |      100 |      100 |                   |
 src                        |      100 |      100 |      100 |      100 |                   |
  __utils.ts                |      100 |      100 |      100 |      100 |                   |
 src/stackoverflow/57960039 |      100 |      100 |      100 |      100 |                   |
  service.ts                |      100 |      100 |      100 |      100 |                   |
----------------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.234s, estimated 6s

这是完成的演示:https ://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/57960039


推荐阅读