使用 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 全局设置文件中。但是在测试文件中尝试直接导入:没有区别。
我试过了:
- 从所有依赖项中清除我的控制器测试(只剩下控制器)。
- 降级 nestjs 包
- 运行 在另一台电脑上测试
- 降级笑话包
软件包依赖项:
"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,然后一切都正确了。
早些时候一切正常,但我已经不知道出了什么问题。 其他测试正确通过。问题仅在于方法参数装饰器。 包依赖足够新鲜。
错误:“类型错误:无法读取未定义的属性(读取 '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 全局设置文件中。但是在测试文件中尝试直接导入:没有区别。
我试过了:
- 从所有依赖项中清除我的控制器测试(只剩下控制器)。
- 降级 nestjs 包
- 运行 在另一台电脑上测试
- 降级笑话包
软件包依赖项:
"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,然后一切都正确了。