mcpField
U
unit-test-bean-validation

unit-test-bean-validation

github.com/giuseppe-trisciuoglioテスト・品質3654.9k 回インストール7 週間前に更新
リポジトリ

unit-test-bean-validation について

JUnit 5 で Jakarta Bean Validation のアノテーションとカスタムバリデーターをテストするガイドです。Spring コンテキストを使わずに制約を単体テストしたい Java 開発者向けです。

主な機能

  1. 01組み込み制約のテスト
  2. 02カスタムバリデーターのテスト
  3. 03フィールド間バリデーション
  4. 04バリデーショングループのテスト
  5. 05パラメータ化テストのパターン
  6. 06違反詳細のアサーション

活用シーン

  1. 01DTO の検証ルールのテスト
  2. 02カスタム制約メッセージの確認
  3. 03パスワード一致ルールの検証
  4. 04条件付きバリデーショングループのテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-bean-validation

インストール後、Claude Code で/unit-test-bean-validationを入力して読み込まれたか確認します。

同梱ファイルreferences/advanced-patterns.mdreferences/custom-validators.md

詳細

Jakarta Bean Validation のユニットテスト

概要

このスキルでは、JUnit 5 を使って Jakarta Bean Validation のアノテーションやカスタムバリデーターをユニットテストするための、実行可能なパターンを紹介します。組み込み制約(@NotNull、@Email、@Min、@Max、@Size)、カスタムの @Constraint 実装、フィールド間検証、検証グループを扱います。テストは Spring のコンテキストなしで独立して実行できます。

使用する場面

  • Jakarta Bean Validation または JSR-380 の制約のユニットテストを作成するとき
  • カスタムの @Constraint バリデーターと制約違反メッセージをテストするとき
  • DTO やリクエストオブジェクトの Bean Validation ロジックをテストするとき
  • フィールド間検証(パスワードの一致など)を検証するとき
  • 検証グループを使った条件付き検証をテストするとき
  • Spring Boot のコンテキストなしで高速な検証テストを行うとき

手順

  1. 依存関係を追加する:jakarta.validation-api と hibernate-validator をテストスコープに含めます
  2. ベーステストクラスを作成する:@BeforeEach で Validation.buildDefaultValidatorFactory() を使い、Validator を一度だけ作成します
  3. まず有効なケースをテストする:オブジェクトが違反なしで通ることを検証します
  4. 無効なケースをテストする:制約違反に正しいプロパティパスとメッセージが含まれることをアサートします
  5. 違反の詳細を取得する:getPropertyPath()、getMessage()、getInvalidValue() を使います
  6. カスタムバリデーターをテストする:パターンについては references/custom-validators.md を参照してください
  7. パラメーター化テストを使う:@ParameterizedTest で複数の入力を効率よくテストします
  8. グループ検証をテストする:条件付きルールには検証グループを使います(references/advanced-patterns.md を参照)

例

Maven の設定
<dependency>
  <groupId>jakarta.validation</groupId>
  <artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.assertj</groupId>
  <artifactId>assertj-core</artifactId>
  <scope>test</scope>
</dependency>
共通のテスト設定
import jakarta.validation.*;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.path.Path;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;

class BaseValidationTest {
  protected Validator validator;

  @BeforeEach
  void setUpValidator() {
    validator = Validation.buildDefaultValidatorFactory().getValidator();
  }
}
基本的な制約のテスト
class UserDtoTest extends BaseValidationTest {

  @Test
  void shouldPassValidationWithValidUser() {
    UserDto user = new UserDto("Alice", "[email protected]", 25);
    assertThat(validator.validate(user)).isEmpty();
  }

  @Test
  void shouldFailWhenNameIsNull() {
    UserDto user = new UserDto(null, "[email protected]", 25);
    assertThat(validator.validate(user))
      .extracting(ConstraintViolation::getMessage)
      .contains("must not be blank");
  }

  @Test
  void shouldFailWhenEmailIsInvalid() {
    UserDto user = new UserDto("Alice", "invalid-email", 25);
    Set<ConstraintViolation<UserDto>> violations = validator.validate(user);
    assertThat(violations)
      .extracting(ConstraintViolation::getPropertyPath)
      .extracting(Path::toString)
      .contains("email");
  }

  @Test
  void shouldFailWhenAgeIsBelowMinimum() {
    UserDto user = new UserDto("Alice", "[email protected]", -1);
    assertThat(validator.validate(user))
      .extracting(ConstraintViolation::getMessage)
      .contains("must be greater than or equal to 0");
  }

  @Test
  void shouldFailWhenMultipleConstraintsViolated() {
    UserDto user = new UserDto(null, "invalid", -5);
    assertThat(validator.validate(user)).hasSize(3);
  }
}
カスタムバリデーターのテスト

カスタム制約のパターンについては、references/custom-validators.md を参照してください:

  • @Constraint アノテーションの作成
  • ConstraintValidator の実装
  • フィールド間検証(パスワードの一致)
  • ステートレスなバリデーターのベストプラクティス
検証グループのテスト

検証グループとパラメーター化テストについては、references/advanced-patterns.md を参照してください:

  • 検証グループインターフェースの定義
  • groups パラメーターを使った条件付き検証
  • @ValueSource と @CsvSource を使った @ParameterizedTest
  • 失敗した検証テストのデバッグ

ベストプラクティス

  • 有効なケースと無効なケースの両方をテストする:すべての制約について、成功するテストケースと失敗するテストケースの両方が必要です
  • 違反の詳細をアサートする:プロパティパス、メッセージ、制約の種類を検証します
  • エッジケースをテストする:null、空文字列、空白のみ、境界値
  • バリデーターをステートレスに保つ:カスタムバリデーターは状態を保持してはいけません
  • わかりやすいメッセージを使う:制約メッセージはユーザーにとってわかりやすいものにします
  • 関連するテストをまとめる:BaseValidationTest を拡張し、バリデーターの設定を共有します
  • エラーメッセージをテストする:メッセージが要件と一致することを確認します

よくある落とし穴

  • null 値のテストを忘れる(ほとんどの制約はデフォルトで null を無視します)
  • 制約違反のプロパティパスを検証しない
  • ユニットレベルではなく、サービス/コントローラーレベルで検証をテストする
  • カスタムバリデーターを複雑にしすぎる
  • 他の制約と組み合わせる必須フィールドに @NotNull がない

制約と注意事項

  • null の処理:ほとんどの制約はデフォルトで null を無視します。必須フィールドには他の制約と @NotNull を組み合わせてください
  • スレッドセーフ:Validator インスタンスはスレッドセーフで、共有できます
  • メッセージのローカライズ:i18n が必要な場合は、異なるロケールでテストしてください
  • カスケード検証:ネストされたオブジェクトに @Valid を使うと、再帰的に検証できます
  • カスタムバリデーター:ステートレスであり、null 値に対して true を返す必要があります
  • テストの独立性:検証のユニットテストは Spring のコンテキストやデータベースに依存させないでください

トラブルシューティング

ValidatorFactory が見つからない:jakarta.validation-api と hibernate-validator がテストクラスパスに含まれていることを確認してください。

カスタムバリデーターが呼び出されない:@Constraint(validatedBy = YourValidator.class) アノテーションが正しいことを確認してください。

null 値が検証を通過する:これは想定どおりの動作です。@NotNull が指定されていない場合、制約は null を無視します。

違反件数が正しくない:hasSize() を使用して正確な件数を確認し、オブジェクト内のすべてのフィールドを確認してください。

プロパティパスが正しくない:制約アノテーションがゲッターではなくフィールドに付いていることを確認してください。

参考資料

コメント