使用 jest 测试 NestJS 控制器时,它在控制器的方法参数装饰器上抛出 TypeError

When testing NestJS controller using jest, it throwns TypeError on controller's method parameter decorator

早些时候一切正常,但我已经不知道出了什么问题。 其他测试正确通过。问题仅在于方法参数装饰器。 包依赖足够新鲜。

错误:“类型错误:无法读取未定义的属性(读取 'prototype')”;

这是要测试的控制器(经过简化,但失败):

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

@Controller('mytest')
export class MyTestController {
  @Post()
  async testMethod(@Body() b: any): Promise<void> {
    return undefined;
  }
}

使用来自“@nestjs/common”的@Res 和@Req 装饰器得到同样的错误。 尝试使用来自 Typescript docs 的自定义 @required 装饰器:同样的错误。

测试:

import { MyTestController } from './mytest-controller';

test('...', async () => {
  const a = new MyTestController();
  expect(true).toBeTruthy();
});

在测试命令 运行 之后,MyTestController 导入期间测试失败。所以连测试都达不到。

如果我删除所有参数装饰器,测试将通过。

reflect-metadata 被导入到 jest 全局设置文件中。但是在测试文件中尝试直接导入:没有区别。

我试过了:

软件包依赖项:

"dependencies": {
    "@nestjs/common": "^8.2.6",
    "@nestjs/core": "^8.2.6",
    "@nestjs/schematics": "^8.0.5",
    "@types/express": "^4.17.12",
    "@types/jsonwebtoken": "^8.5.1",
    "@types/supertest": "^2.0.11",
    "argon2": "^0.28.3",
    "dotenv": "^14.3.2",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1",
    "reflect-metadata": "^0.1.13",
    "rxjs": "^7.5.2",
    "ts-node": "^10.7.0",
    "typeorm": "^0.2.41",
    "uuid": "^8.3.2"
  },
  "devDependencies": {
    "@babel/cli": "^7.16.8",
    "@babel/core": "^7.16.12",
    "@babel/plugin-proposal-class-properties": "^7.16.7",
    "@babel/plugin-proposal-decorators": "^7.16.7",
    "@babel/plugin-proposal-export-default-from": "^7.16.7",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-typescript": "^7.16.7",
    "@nestjs/testing": "8.2.6",
    "@types/jest": "^27.4.1",
    "@types/uuid": "^8.3.4",
    "@typescript-eslint/eslint-plugin": "^5.10.1",
    "@typescript-eslint/parser": "^5.10.1",
    "babel-jest": "^27.5.1",
    "babel-plugin-parameter-decorator": "^1.0.16",
    "eslint": "^8.7.0",
    "eslint-config-airbnb-base": "^15.0.0",
    "eslint-plugin-import": "^2.25.4",
    "eslint-plugin-jest": "^26.0.0",
    "jest": "^27.5.1",
    "supertest": "^6.2.2",
    "testcontainers": "^8.6.1",
    "ts-jest": "^27.1.4",
    "typescript": "^4.5.5"
  }

有什么想法吗?

试图将控制器和它的测试合并到一个文件中:出错。

尝试在没有控制器的情况下使用装饰器(只是 class),如下所示,没有错误

import 'reflect-metadata';

const requiredMetadataKey = Symbol('required');

function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
  const existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
  existingRequiredParameters.push(parameterIndex);
  Reflect.defineMetadata(requiredMetadataKey, existingRequiredParameters, target, propertyKey);
}

class A {
  do(@required par: any): void {
    return undefined;
  }
}

test('...', async () => {
  const a = new A();
  a.do({});
  expect(true).toBeTruthy();
});

所以 nestjs 控制器和任何方法参数装饰器的捆绑有问题。

从 MyTestController 中删除 @Controller() 和 @Post() 装饰器,但使用 @Body() 装饰器:没有错误。

我可以假设 babel-jest 有问题,它将 ts 转换为旧的 js 样式。 我的 babel 配置:

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: {
          "node": "current"
        }
      }
    ],
    "@babel/preset-typescript"
  ],
  plugins: [
    ["@babel/plugin-proposal-decorators", {"legacy": true}],
    ["@babel/plugin-proposal-class-properties"],
    "@babel/plugin-proposal-export-default-from",
    "babel-plugin-parameter-decorator",
  ],
}

但是,在我看来,babel 配置正确

所以,我在 babel 中找不到错误。所以我找到了另一个名为 'ts-jest' 的转换器,并在 jest 配置文件中用 in 替换了 babel-jest:

transform: {
    "\.ts$": 'ts-jest',
  },

但在那之后 jest-setup-after-end、global-setup 和 global-teardown 文件中出现关于导入的错误(例如:您不能在模块外使用 'import' 语句)。我不得不将这些文件从 .js 转换为 .ts,将它们添加到“include”部分的 tsconfig.json,然后一切都正确了。