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

Posted

技术标签:

【中文标题】使用正确类型使用 Jest 和 Typescript 模拟 Express 请求【英文标题】:Mocking Express Request with Jest and Typescript using correct types 【发布时间】:2020-01-17 17:32:13 【问题描述】:

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

但是,如果我改变:

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

到:

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

根据 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.

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

【问题讨论】:

您对Partial&lt;Request&gt; 所做的事情是正确的,但我敢打赌,问题在于userRegister 不接受Partial&lt;Request&gt; - 它需要Request 对吗?您可以将 mockRequest 声明为 Request 吗? const mockRequest: Partial&lt;Request&gt; ... 如果没有,你可以给我们一个断言:await userRegister(mockRequest as Request, mockResponse, mockNext); 现在看起来像这样:export const userRegister = async ( req: express.Request, res: express.Response, next: express.NextFunction ) =&gt; 。模拟在测试中。我现在通过使用:const mockRequest: any = body: firstName: 'J', lastName: 'Doe', email: 'jdoe@abc123.com', password: 'Abcd1234', passwordConfirm: 'Abcd1234', company: 'ABC Inc.', , ; 解决了这个问题。但我被告知尽量不要使用 any,因为它违背了 TypeScript 的目的。 啊啊啊。我知道了。是的,userRegister 确实需要 req: express.Requestres: express.Response。有趣,但对于测试,有没有办法解决这个问题?我想我在any 工作时感到困惑。 你能把userRegister函数贴在这里,让我们看看这个函数的类型声明吗? 【参考方案1】:

似乎@kschaer 说userRegister 是问题所在。如果您希望该功能采用Partial&lt;Request&gt;,您可以将userRegister 更改为:

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

但由于这仅用于测试,您也可以将 mockRequest 转换为 Request 类型,如下所示:

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

希望对您有所帮助。

【讨论】:

当我这样做时,我现在得到这个 TS 错误:Type ' body: firstName: string; lastName: string; email: string; password: string; passwordConfirm: string; company: string; ; ' is not assignable to type '&lt;Request&gt;() =&gt; any'. Object literal may only specify known properties, and 'body' does not exist in type '&lt;Request&gt;() =&gt; any'. 嗯,那么不确定。对不起。【参考方案2】:

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

您需要的是type assertion。这是一种告诉 TypeScript “好的兄弟,我知道我在这里做什么。”

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

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

如果在测试期间发生崩溃,因为mockRequest 与 Request 不够相似,我们会知道并修复模拟,添加一些新属性等。

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

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

当我们想测试我们的代码在输入错误的情况下不能很好地工作时,它很有用。

对于你的特殊情况——模拟快递请求——有jest-express可以帮助你,当然如果你可以节省 node_modules 的大小。

【讨论】:

我知道有事!感谢您的提醒。 现在当我调用 userRegister 函数时,我得到了这个 TS 错误 Argument of type 'Request' is not assignable to parameter of type 'import("/Users/nrb/Development/partsync-server/node_modules/@types/express/index").Request'. Type 'Request' is missing the following properties from type 'Request': get, header, accepts, acceptsCharsets, and 68 more.ts(2345) 嘿尼克B。你有两个请求。一个是全局的,在@types/node 中输入,另一个来自 Express。这有点烦人。 typescriptlang.org/play/… 如果它仍然中断,您可以简单地将as Request 替换为as any as Request 或简单地as any。太感谢了。很好的答案。

以上是关于使用正确类型使用 Jest 和 Typescript 模拟 Express 请求的主要内容,如果未能解决你的问题,请参考以下文章

通过 npm 脚本从 Powershell 使用 --collectCoverageFrom 运行 Jest 测试时,转义引号和斜杠的正确方法是啥?

Jest、Typescript、ts-jest:覆盖范围略有不正确

前端开发工具集:单元测试(jest)

如何使用 Jest 正确测试被拒绝的承诺?

如何用 Jest 正确实现这样的测试套件架构?

如何使用 Jest 测试部分对象?