class-validatorを使用したcustom-decoratorの作成とテスト

今回やること

nest.jsを使用してhttpリクエストを送る際にdtoを使用してリクエストを送るのにdtoのバリデーションとしてclass-validatorを使用します

github.com

その際に用意されたdecoratorだけでなく自前で実装をしたい際にcustom-decoratorを実装すると思うのですがその作成とテストまでの簡単な作り方を紹介したいと思います。

最後にgithubソースコードも残しておくのでそちらも参考にして頂ければと思います。 ※途中で調べている間に見つけたのですが@Matchesというdecoratorがありそれを使えば同じことができました😭 こういうふうに作るんだよっていう感じで見ていただけると嬉しいです

準備

nest cliのinstallとnest newでアプリケーションを作成しましょう

$ npm i -g @nestjs/cli

$ nest new project-name

こうすることでnestjsの雛形が作成されるのでアプリケーションのディレクトリに移動しましょう

今回は特に機能の作成をするのではなくdtoのみにフォーカスするのでsrc配下に作成していきます Personというdtoを作成します

$ mkdir src/dto

$ touch src/dto/person.dto.ts

このような簡単なDTOを作成します

export class PersonDto {
  readonly name: string;

  readonly nameKana: string;

  readonly age: number;
}

class-validatorのinstallをします

$ yarn add class-validator class-transformer

person.dto.tsにdecoratorを付与します

import { IsString, IsNotEmpty, IsNumber } from 'class-validator';

export class PersonDto {
  @IsString()
  @IsNotEmpty()
  readonly name: string;

  @IsString()
  @IsNotEmpty()
  readonly nameKana: string;

  @IsNumber()
  @IsNotEmpty()
  readonly age: number;
}

これで準備完了です。

今回のdto見てみると nameKanaにカナであるかどうかのvaldatationも欲しいなと思うのでそのdecoratorを作成していきます

$ mkdir src/share
$ mkdir src/share/custom-decorator
$ touch src/share/custom-decorator/IsKana.ts

こちらを参考に作成しました

GitHub - typestack/class-validator: Validation made easy using TypeScript decorators.

import {
  ValidatorConstraint,
  ValidationOptions,
  registerDecorator,
  ValidatorConstraintInterface,
  ValidationArguments,
} from 'class-validator';

const regexp = /^[ァ-ー]+$/;

@ValidatorConstraint()
export class IsKanaConstraint implements ValidatorConstraintInterface {
  validate(text: string, args: ValidationArguments): boolean {
    return regexp.test(text);
  }

  defaultMessage(args: ValidationArguments): string {
    return 'nameKana should be a katakana';
  }
}

export function IsKana(
  property: string,
  validationOptions?: ValidationOptions,
) {
  return function(object: object, propertyName: string) {
    registerDecorator({
      name: 'IsKana',
      target: object.constructor,
      propertyName,
      constraints: [],
      options: validationOptions,
      validator: IsKanaConstraint,
    });
  };
}

こちらでカタカナを判定し、validateを使用して判定しています

const regexp = /^[ァ-ー]+$/;

validate(text: string, args: ValidationArguments): boolean {
    return regexp.test(text);
}

何かしらRESTAPIを作成して試すのもいいのですが今回はテストコードを簡単に作成してエラーを吐くようにしてconsole.logで中身を見てみたいと思います。

import { Validator } from 'class-validator';
import { IsKana } from './IsKana';

const validator = new Validator();

describe('IsKana', () => {
  class MyClass {
    @IsKana('kana')
    someString: string;
  }


  it('should be invalid', () => {
    const obj = new MyClass();
    obj.someString = 'てすと';
    return validator.validate(obj).then(errors => {
      expect(errors.length).toBe(1);
    });
  });
});
$ yarn test src/share/class-validator/custom-decorator/IsKana.spec.ts

console.log src/share/class-validator/custom-decorator/IsKana.spec.ts:24
    [
      ValidationError {
        target: MyClass { someString: 'てすと' },
        value: 'てすと',
        property: 'someString',
        children: [],
        constraints: { IsKanaConstraint: 'nameKana should be a katakana' }
      }
    ]

このように平仮名の際はエラーが返ってきました

今度はカタカナもたしてテストを

  it('should be valid', () => {
    const obj = new MyClass();
    obj.someString = 'テスト';
    return validator.validate(obj).then(errors => {
      console.log(errors);
      expect(errors.length).toBe(0);
    });
  });
console.log src/share/class-validator/custom-decorator/IsKana.spec.ts:16
    []

エラーは返ってこなかったので正しくできています。

最後にdtoでimportして反映しておきましょう

import { IsString, IsNotEmpty, IsNumber } from 'class-validator';
import { IsKana } from 'src/share/class-validator';

export class PersonDto {
  @IsString()
  @IsNotEmpty()
  readonly name: string;

  @IsString()
  @IsNotEmpty()
  @IsKana('kana')
  readonly nameKana: string;

  @IsNumber()
  @IsNotEmpty()
  readonly age: number;
}

ということでこれでcustom-decoratorの作成とテストまでです。 初めてtypescriptを触って右も左も分からない感じでもドキュメントが豊富なので最低限はできるなあという印象でした。

またnestjsが非常に豊富なpackageを用意してくれているので簡単にテストを作れるのも魅力だなと思います。

こちらにgithubレポジトリを置いておきますので何かの参考になれば嬉しいです

GitHub - yuzujoe/class-validation-test