gpt4 book ai didi

validation - 如何使用 NestJS 和类验证器手动测试输入验证

转载 作者:行者123 更新时间:2023-11-28 21:15:36 25 4
gpt4 key购买 nike

TLNR:我试图在 Controller 规范中测试 DTO 验证,而不是在专门为此设计的 e2e 规范中。 McDoniel 的回答为我指明了正确的方向。


我开发了一个 NestJS 入口点,看起来像这样:

@Post()
async doStuff(@Body() dto: MyDto): Promise<string> {
// some code...
}

我使用 class-validator 以便当我的 API 收到请求时,有效负载被解析并转换为 MyDto 对象,并执行在 MyDto 类中作为注释出现的验证。请注意,MyDto 有一个 MySubDto 类的嵌套对象数组。使用 @ValidateNested 和 @Type 注释,嵌套对象也可以正确验证。

效果很好。

现在我想为执行的验证编写测试。在我的 .spec 文件中,我写道:

import { validate  } from 'class-validator';
// ...
it('should FAIL on invalid DTO', async () => {
const dto = {
//...
};
const errors = await validate( dto );
expect(errors.length).not.toBe(0);
}

这会失败,因为经过验证的 dto 对象不是 MyDto。我可以这样重写测试:

it('should FAIL on invalid DTO', async () => {
const dto = new MyDto()
dto.attribute1 = 1;
dto.subDto = { 'name':'Vincent' };
const errors = await validate( dto );
expect(errors.length).not.toBe(0);
}

现在可以在 MyDto 对象上正确进行验证,但不能在我的嵌套 subDto 对象上进行验证,这意味着我将不得不使用相应的类实例化我的 Dto 的 aaaall 对象,这会非常低效。此外,实例化类意味着如果我故意省略一些必需的属性或指示不正确的值,TypeScript 将引发错误。

那么问题是:

如何在我的测试中使用 NestJs 内置的请求主体解析器,以便我可以为 dto 编写我想要的任何 JSON,将其解析为 MyDto 对象并使用 对其进行验证class-validator 验证函数?

也欢迎任何其他更好的测试验证方法!

最佳答案

虽然,我们应该测试我们的验证 DTO 如何与 ValidationPipe 一起工作,这是一种集成或 e2e 测试的形式。单元测试就是单元测试,对吧?!每个单元都应该可以独立测试。

Nest.js 中的 DTO 非常unit-tastable。当 DTO 包含复杂的正则表达式或卫生逻辑时,有必要对 DTO 进行单元测试。


为测试创建一个DTO对象

您正在寻找的 Nest.js 中的请求主体解析器是 class-transformer 包。它有一个函数 plainToInstance() 可以将您的文字或 JSON 对象转换为指定类型的对象。在您的示例中,指定的类型是您的 DTO 的类型:

const myDtoObject = plainToInstance(MyDto, myBodyObject)

在这里,myBodyObject 是您为测试创建的普通对象,例如:

const myBodyObject = { attribute1: 1, subDto: { name: 'Vincent' } }

plainToInstance() 函数还应用您在 DTO 中的所有转换。如果你只是想测试转换,你可以在这个语句之后断言。您不必调用 validate() 函数来测试转换。


在测试中验证 DTO 的对象

要模拟 Nest.js 的验证,只需将 myDtoObject 传递给 class-validator 包的 validate() 函数:

const errors = await validate(myDtoObject)

此外,如果您的 DTO 或 SubDTO 对象太大或太复杂而无法创建,您可以选择跳过剩余的属性或子对象,例如您的 subDto:

const errors = await validate(myDtoObject, { skipMissingProperties: true })

现在您的测试对象可以没有 subDto,例如:

const myBodyObject = { attribute1: 1 }

断言错误

除了断言 errors 数组不为空之外,我还想为 DTO 中的每个验证指定一条自定义错误消息:

@IsPositive({ message: `Attribute1 must be a positive number.` })
readonly attribute1: number

自定义错误消息的一个优点是我们可以用一种用户友好的方式编写它,而不是库创建的通用消息。另一个很大的优势是我可以在我的测试中断言这个错误消息。这样我可以确定 errors 数组不为空,因为它包含此特定验证的错误而不是其他内容:

expect(stringified(errors)).toContain(`Attribute1 must be a positive number.`)

这里,stringified() 是一个简单的实用函数,用于将错误对象转换为 JSON 字符串,因此我们可以在其中搜索错误消息:

export function stringified(errors: ValidationError[]): string {
return JSON.stringify(errors)
}

你的最终测试代码

代替 controller.spec.ts 文件,创建一个特定于您的 DTO 的新文件,例如用于 DTO 单元测试的 my-dto.spec.ts . DTO 可以有大量的单元测试,它们不应该与 Controller 的测试混合:

it('should fail on invalid DTO', async () => {
const myBodyObject = { attribute1: -1, subDto: { name: 'Vincent' } }
const myDtoObject = plainToInstance(MyDto, myBodyObject)
const errors = await validate(myDtoObject)
expect(errors.length).not.toBe(0)
expect(stringified(errors)).toContain(`Attribute1 must be a positive number.`)
}

请注意您不必为创建 myDtoObject 将值一一分配给属性。在大多数情况下,您的 DTO 的属性应标记为 readonly。所以,你不能一个一个地赋值。 plainToInstance() 来拯救!


就是这样!您几乎就在那里,对您的 DTO 进行单元测试。好努力!希望现在有所帮助。

关于validation - 如何使用 NestJS 和类验证器手动测试输入验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58843038/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com