使用正确的类型使用 Jest 和 Typescript 模拟 Express 请求

Bor*_*uhh 21 express typescript jestjs

我在 Jest 中获取正确的 Express Request 类型时遇到了一些麻烦。我有一个使用此代码传递的简单用户注册:

import { userRegister } from '../../controllers/user';
import { Request, Response, NextFunction } from 'express';

describe('User Registration', () => {
  test('User has an invalid first name', async () => {
    const mockRequest: any = {
      body: {
        firstName: 'J',
        lastName: 'Doe',
        email: 'jdoe@abc123.com',
        password: 'Abcd1234',
        passwordConfirm: 'Abcd1234',
        company: 'ABC Inc.',
      },
    };

    const mockResponse: any = {
      json: jest.fn(),
      status: jest.fn(),
    };

    const mockNext: NextFunction = jest.fn();

    await userRegister(mockRequest, mockResponse, mockNext);

    expect(mockNext).toHaveBeenCalledTimes(1);
    expect(mockNext).toHaveBeenCalledWith(
      new Error('First name must be between 2 and 50 characters')
    );
  });
});
Run Code Online (Sandbox Code Playgroud)

但是,如果我改变:

    const mockRequest: any = {
      body: {
        firstName: 'J',
        lastName: 'Doe',
        email: 'jdoe@abc123.com',
        password: 'Abcd1234',
        passwordConfirm: 'Abcd1234',
        company: 'ABC Inc.',
      },
    };
Run Code Online (Sandbox Code Playgroud)

到:

const mockRequest: Partial<Request> = {
  body: {
    firstName: 'J',
    lastName: 'Doe',
    email: 'jdoe@abc123.com',
    password: 'Abcd1234',
    passwordConfirm: 'Abcd1234',
    company: 'ABC Inc.',
  },
};
Run Code Online (Sandbox Code Playgroud)

从 TypeScript 文档 ( https://www.typescriptlang.org/docs/handbook/utility-types.html#partialt ) 中,这应该使 Request 对象上的所有字段都是可选的。

但是,我收到此错误:

Argument of type 'Partial<Request>' is not assignable to parameter of type 'Request'.
  Property '[Symbol.asyncIterator]' is missing in type 'Partial<Request>' but required in type 'Request'.ts(2345)
stream.d.ts(101, 13): '[Symbol.asyncIterator]' is declared here.
Run Code Online (Sandbox Code Playgroud)

我希望有更多 TypeScript 经验的人可以发表评论并让我知道我做错了什么。

has*_*rus 38

您的模拟数据类型不必完全适合实际数据。好吧,它不是根据定义。这只是个噱头,对吧?

您需要的是类型断言。这是告诉 TypeScript 的一种方式:“好吧,兄弟,我知道我在这里做什么。” .

这不是生产代码,而是测试。您甚至可能在监视模式下运行它。我们可以在这里毫无问题地拒绝某些类型安全。TypeScript 不知道这是一个模拟,但我们知道。

const mockRequest = {
    body: {
    firstName: 'J',
    lastName: 'Doe',
    email: 'jdoe@abc123.com',
    password: 'Abcd1234',
    passwordConfirm: 'Abcd1234',
    company: 'ABC Inc.',
    },
} as Request;
Run Code Online (Sandbox Code Playgroud)

如果在测试过程中出现崩溃,因为mockRequest与 Request 不够相似,我们会知道并且我们将修复模拟,添加一些新属性等。

如果as Request不起作用,您可以通过声明 to或first 然后声明您需要的类型来告诉 TypeScript “我真的知道我在这里做什么”。它看起来像anyunknown

const x: number = "not a number :wink:" as any as number;
Run Code Online (Sandbox Code Playgroud)

当我们想测试我们的代码在错误输入下是否不能正常工作时,它很有用。

对于您的特定情况 -模拟express 请求 -如果您可以节省 node_modules 的大小,那么jest-express可以帮助您。

  • 如果仍然失败,您可以简单地将“as Request”替换为“as any as Request”或简单地“as any”。太感谢了。很好的答案。 (3认同)

And*_*ira 7

为了将来搜索这个主题,我建议查看这个库:https://www.npmjs.com/package/node-mocks-http

该库具有为 Express 框架的请求和响应创建模拟对象的方法,这对我帮助很大,并且是我发现的简单方法。

简单的单元测试示例:

import { Request, Response } from 'express';
import {
  createRequest, createResponse, MockRequest, MockResponse,
} from 'node-mocks-http';
import { AppController } from './app-controller';
import { APP_NAME, APP_VERSION } from '../../constants';

describe('AppController - UnitTestCase', () => {
  let controller: AppController;
  let request: MockRequest<Request>;
  let response: MockResponse<Response>;
  beforeEach(() => {
    controller = new AppController();
    /** Response Mock */
    response = createResponse();
  });

  it('should be defined', () => {
    expect(controller).toBeDefined();
  });

  describe('GET /', () => {
    it('should return 200 and API Name + API Version', (done) => {
      /** Request Mock */
      request = createRequest({
        method: 'GET',
        url: '/',
      });

      AppController.index(request, response);

      const body = { app: `${APP_NAME}:${APP_VERSION}` };
      const result = response._getJSONData();
      expect(result).toMatchObject(body);
      expect(result.app).toEqual(body.app);
      expect(response.getHeaders()).toHaveProperty('content-type');
      console.log('headers', response.getHeaders());
      console.log('response body', result);
      done();
    });
  });
});
Run Code Online (Sandbox Code Playgroud)