测试用例可以在Postman中工作,但不能在Visual Studio Code或Jest命令行中工作。
请求Body在Postman中工作并返回所有4个错误:
{ "Item": {} }
缺少FileName和Item。数据字段。
API.dto.ts
import { Type } from 'class-transformer';
import { IsNumberString, IsString, MinLength, ValidateNested } from 'class-validator';
// Expected Payload
// {
// FileName: 'abc',
// Item: {
// Data: '123'
// }
// }
export class ItemDataDTO {
@IsNumberString() @MinLength(2) public readonly Data: string;
}
/**
* This class is the Data Object for the API route
*/
export class ApiDTO {
@IsString() @MinLength(1) public readonly FileName: string;
@ValidateNested()
@Type(() => ItemDataDTO)
Item: ItemDataDTO;
}
api.controller.ts
import { Body, Controller, Get, Options, Put, Request, Response } from '@nestjs/common';
import { ApiDTO } from './api.dto';
@Controller('api')
export class ApiController {
constructor() { }
@Put('donotuse')
public DoNotUse(@Body() APIBody: ApiDTO) {
return 'OK';
}
}
API.dto.spec.ts
import { ArgumentMetadata, ValidationPipe } from '@nestjs/common';
import { ApiDTO } from './api.dto';
describe('ApiDto', () => {
it('should be defined', () => {
expect(new ApiDTO()).toBeDefined();
});
it('should validate the ApiDTO definition', async () => {
const target: ValidationPipe = new ValidationPipe({
transform: true,
whitelist: true,
});
const metadata: ArgumentMetadata = {
type: 'body',
metatype: ApiDTO,
data: '{ "Item": {} }',
};
const Expected: string[] = [
'FileName must be longer than or equal to 1 characters',
'FileName must be a string',
'Item.Data must be longer than or equal to 2 characters',
'Item.Data must be a number string',
];
await target.transform(<ApiDTO>{}, metadata).catch((err) => {
expect(err.getResponse().message).toEqual(Expected);
});
});
});
期望失败。
await target.transform(<ApiDTO>{}, metadata).catch((err) => {
expect(err.getResponse().message).toEqual(Expected);
});
返回2个FileName错误,但没有Item。数据字段。设置data: '',
为data: '{ "Item": {} }'
同样失败
实际期望失败:
expect(received).toEqual(expected) // deep equality
- Expected - 2
+ Received + 0
Array [
"FileName must be longer than or equal to 1 characters",
"FileName must be a string",
- "Item.Data must be longer than or equal to 2 characters",
- "Item.Data must be a number string",
]
这表明文件名验证在那里,返回那两行,但是Item。数据错误,不会返回,并且在我的测试用例结果中是"额外的"。
然而,通过Postman, PUT/api/donotuse调用这个请求体:
{ "Item": {} }
返回所有4个错误。HTTP状态码也是一个400 Bad Request,因为NestJS通常会自己返回。我不确定在我的测试用例中有什么问题,以获得所有返回的错误。
编辑我也试着通过端到端测试来做到这一点,正如答案所建议的,但我仍然收到相同的缺失错误。
describe('ApiDto - E2E', () => {
let app: INestApplication;
afterAll(async () => {
await app.close();
});
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
app.useGlobalPipes(new ValidationPipe({ transform: true, errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }));
await app.init();
});
it('should validate the ApiDTO definition', async () => {
const APIRequestDTO: unknown = { FileName: null, Item: {} };
const ResponseData$ = await request(app.getHttpServer())
.put('/api/donotuse')
.set('Accept', 'application/json')
.send(APIRequestDTO as ApiDTO);
const Expected: string[] = [
'FileName must be longer than or equal to 1 characters',
'FileName must be a string',
'Item.Data must be longer than or equal to 2 characters',
'Item.Data must be a number string',
];
expect(ResponseData$.status).toBe( HttpStatus.UNPROCESSABLE_ENTITY);
expect(ResponseData$.body.message).toBe(Expected);
});
});
这仍然没有提供从Postman调用正确返回的所有错误。我不确定在测试期间发生了什么,子类型没有被处理。通过Postman,相同的body,相同的header等调用它,确实会返回正确的错误:
"message"(文件名必须大于或等于1个字符;"文件名"必须是字符串;"项目。数据长度必须大于或等于2个字符。"项目。数据必须是一个数字字符串"),
我知道它也会进入ValidationPipe,因为我的自定义错误代码,返回422 Unprocessable Entity,表明这是失败的验证。在我的单元测试和E2E测试中都返回了相同的错误,但没有返回关于Item.Data的第二组错误。
我假设在你的应用程序中,你正在全局注册ValidationPipe,例如:app.useGlobalPipes(new ValidationPipe());
由于Global Pipes注册的位置,它们将在您对后端执行实际请求时工作,但不会在测试中拾取。这就是为什么你看到它在Postman中工作,而不是在Jest中。
如果您希望在测试中使用Validation管道,则需要手动设置它,如下所示:
// Probably in your beforeEach where you're setting up the test module
const app = moduleFixture.createNestApplication();
app.useGlobalPipes(new ValidationPipe());
如何在端到端测试期间应用全局管道的副本