Search code examples
validationtestingdtonestjs

How to manually test input validation with NestJS and class-validator


TLNR: I was trying to test DTO validation in the controller spec instead of in e2e specs, which are precisely crafted for that. McDoniel's answer pointed me to the right direction.


I develop a NestJS entrypoint, looking like that:

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

I use class-validator so that when my API receives a request, the payload is parsed and turned into a MyDto object, and validations present as annotations in MyDto class are performed. Note that MyDto has an array of nested object of class MySubDto. With the @ValidateNested and @Type annotations, the nested objects are also validated correctly.

This works great.

Now I want to write tests for the performed validations. In my .spec file, I write:

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);
}

This fails because the validated dto object is not a MyDto. I can rewrite the test as such:

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);
}

Validations are now properly made on the MyDto object, but not on my nested subDto object, which means I will have to instantiate aaaall objects of my Dto with according classes, which would be much inefficient. Also, instantiating classes means that TypeScript will raise errors if I voluntarily omits some required properties or indicate incorrect values.

So the question is:

How can I use NestJs built-in request body parser in my tests, so that I can write any JSON I want for dto, parse it as a MyDto object and validate it with class-validator validate function?

Any alternate better-practice ways to tests validations are welcome too!


Solution

  • To test input validation with the validation pipes, I think it is agreed that the best place to do this is in e2e tests rather than in unit tests, just make sure that you remember to register your pipes (if you normally use app.useGlobalPipes() instead of using dependency injection)