简体   繁体   English

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

[英]Mocking Express Request with Jest and Typescript using correct types

I have been having some trouble getting the correct Express Request type working in Jest.我在 Jest 中获得正确的 Express Request 类型时遇到了一些麻烦。 I have a simple user registration passing with this code:我有一个使用此代码传递的简单用户注册:

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

However, if I change:但是,如果我改变:

    const mockRequest: any = {
      body: {
        firstName: 'J',
        lastName: 'Doe',
        email: 'jdoe@abc123.com',
        password: 'Abcd1234',
        passwordConfirm: 'Abcd1234',
        company: 'ABC Inc.',
      },
    };

to:至:

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

From the TypeScript documentation ( https://www.typescriptlang.org/docs/handbook/utility-types.html#partialt ), this should make all fields on the Request object optional.从 TypeScript 文档 ( https://www.typescriptlang.org/docs/handbook/utility-types.html#partialt ) 来看,这应该使 Request 对象上的所有字段都是可选的。

However, I get this error:但是,我收到此错误:

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.

I was hoping that someone with a little more TypeScript experience could comment and let me know what I am doing wrong.我希望有更多 TypeScript 经验的人可以发表评论并让我知道我做错了什么。

Your mock data type doesn't have to perfectly fit the actual data.您的模拟数据类型不必完全适合实际数据。 Well, it doesn't by definition.好吧,它不是根据定义。 It's just a mock, right?这只是一个模拟,对吧?

What you need is a type assertion .您需要的是类型断言 It's a way to tell TypeScript "Okay bro, I know what I'm doing here."这是一种告诉 TypeScript “好的兄弟,我知道我在这里做什么”的方式。 . .

This is not a production code, it's a test.这不是生产代码,而是测试。 You're probably even running it in watch mode.您甚至可能在手表模式下运行它。 We can reject some type safety here without problem.我们可以在这里毫无问题地拒绝某些类型安全。 TypeScript doesn't know it's a mock, but we do. TypeScript 不知道它是一个模拟,但我们知道。

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

If something crashes during the test, because mockRequest isn't similar to Request enough, we'll know and we'll fix the mock, add some new properties etc.如果在测试过程中发生崩溃,因为mockRequest与 Request 不够相似,我们会知道并修复 mock,添加一些新属性等。

If as Request doesn't work you can tell TypeScript "I REALLY know what I'm doing here" by asserting to any or unknown first and then to the type you need.如果as Request不起作用,你可以告诉 TypeScript “我真的知道我在这里做什么” ,首先断言anyunknown然后再断言你需要的类型。 It would look like它看起来像

const x: number = "not a number :wink:" as any as number;

It's useful when we'd like to test that our code doesn't work well with bad input.当我们想测试我们的代码在输入错误的情况下不能很好地工作时,它很有用。

For your particular case -- mocking express Request -- there is jest-express to help you, if you can spare the node_modules size of course.对于您的特定情况 - 模拟快递请求 - 如果您当然可以节省 node_modules 的大小,则可以使用jest-express来帮助您。

For future search about this theme, I recommend seeing this library: https://www.npmjs.com/package/node-mocks-http为了将来搜索这个主题,我建议查看这个库: https ://www.npmjs.com/package/node-mocks-http

This library has methods to create mocked objects for Request and Response of the Express Framework, which helped me a lot and was the easy way I found.该库具有为 Express 框架的请求和响应创建模拟对象的方法,这对我有很大帮助,并且是我找到的简单方法。

Simple unit test example:简单的单元测试示例:

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

Seems like userRegister is the problem as @kschaer said.似乎userRegister是@kschaer 所说的问题。 If you want that function to take in a Partial<Request> you can change userRegister to:如果您希望该函数接受Partial<Request> ,您可以将userRegister更改为:

const userRegister = async (req: Partial<Request>, res: Response, next: NextFunction) => { /* snippet */ }

But since this is just for tests you could also cast mockRequest to the Request type like this:但由于这只是用于测试,您也可以将mockRequest转换为Request类型,如下所示:

const mockRequest = <Request>{
  body: {
    /* snippet */
  }
};

Hopefully that helps.希望这会有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM