{"id":50582132,"url":"https://github.com/irof/spring-payload-binding-test","last_synced_at":"2026-06-05T03:30:36.079Z","repository":{"id":357427301,"uuid":"1236820556","full_name":"irof/spring-payload-binding-test","owner":"irof","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-12T17:47:57.000Z","size":126,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-12T19:34:43.453Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/irof.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-12T15:50:39.000Z","updated_at":"2026-05-12T17:48:31.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/irof/spring-payload-binding-test","commit_stats":null,"previous_names":["irof/spring-payload-binding-test"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/irof/spring-payload-binding-test","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irof%2Fspring-payload-binding-test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irof%2Fspring-payload-binding-test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irof%2Fspring-payload-binding-test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irof%2Fspring-payload-binding-test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/irof","download_url":"https://codeload.github.com/irof/spring-payload-binding-test/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irof%2Fspring-payload-binding-test/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33928630,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-05T02:00:06.157Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-05T03:30:34.730Z","updated_at":"2026-06-05T03:30:36.071Z","avatar_url":"https://github.com/irof.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# spring-payload-binding-test\n\nSpring Bootのエンドポイントに使用されるクラスに対して一律JSONバインディングのテストを行うツールです。\n\nSpring Boot 3.5.x(Jackson2) / 4.0.x(Jackson 2 \u0026 3) で利用できます。\n\n## 仕組み\n\n```mermaid\nflowchart TB\n  subgraph Collection[1. 型の収集]\n    direction TB\n    C1[RestControllerのRequest/Responseを収集]\n  end\n\n  subgraph Prepare[2. JSON準備]\n    direction TB\n    E1[\"fixtureファイルが存在するか？\"]\n    E1 -- Yes --\u003e E2-1[ファイルを読み込む]\n    E1 -- No --\u003e E2-2[エンジンで生成する]\n\n    E2-2 --\u003e EW{\"writeMissingFiles\"}\n    EW -- Yes --\u003e T_write[\"ファイルを保存する\"]\n  end\n\n  subgraph Execution[3. ラインドトリップテスト]\n    direction TB\n    T1[\"JSON（IN） -\u003e Java Object\"]\n    T2[\"Java Object -\u003e JSON（OUT）\"]\n    TC{\"JSON IN/OUT が等価か？\"}\n    T1 --\u003e T2 --\u003e TC\n    TC -- NO --\u003e T_error[Assertion Error]\n    TC -- Yes --\u003e T_success\n    T_success[\"Success\"]\n  end\n\n  Collection --\u003e Prepare\n  Prepare --\u003e Execution\n```\n\n## Getting Started\n\n1. 依存を追加する\n2. テストクラスを作成する\n\n### 依存を追加する\n\n`com.github.irof:spring-payload-binding-test` をテストスコープで依存に追加します。\nSpring Boot のバージョンは利用側アプリの設定に従います。\n\nGradle (`build.gradle.kts`):\n\n```kotlin\ndependencies {\n    testImplementation(\"com.github.irof:spring-payload-binding-test:0.0.2\")\n}\n```\n\nMaven (`pom.xml`):\n\n```xml\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.github.irof\u003c/groupId\u003e\n        \u003cartifactId\u003espring-payload-binding-test\u003c/artifactId\u003e\n        \u003cversion\u003e0.0.2\u003c/version\u003e\n        \u003cscope\u003etest\u003c/scope\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n### テストクラスを作成する\n\n`JsonBindingContractTestBase` を継承した空のサブクラスを記述します。\n\n```java\n@SpringBootTest\nclass JsonBindingContractTest extends JsonBindingContractTestBase {\n}\n```\n\nこれで `@RestController` の入出力の型が自動収集され、それぞれに対して JSON バインディングの検証が行われる。\n\n## 検証内容\n\n各ペイロード型に対して deserialize → serialize → 元 JSON と等価性比較というラウンドトリップ検査を行う。\nリクエスト/レスポンスの区別はせず、エンドポイントから収集された型集合に対して一律に検査する。\n同じ型が複数エンドポイントで使われる場合は1回だけ検査する。\n\n### 対象外\n以下は自動的に除外されます。\n\n- フレームワーク提供のハンドラ (`BasicErrorController` 等) \n- `Resource` / `MultipartFile` 等のフレームワーク型 (`org.springframework.*` / `jakarta.*` / `javax.*`) \n\n## 設定など\n\n### バリエーション\n\nテストに使用されるデータのパターンをバリエーションと呼んでいます。\n各ペイロード型に対して複数のバリエーションのテストを行うようになっています。\n\nテスト対象のバリエーションは `variations` メソッドをオーバーライドすることで変更できます。\nデフォルトではビルドインバリエーションが設定されています。\n\n#### ビルトインバリエーション\n\n| バリエーション | 生成される JSON | 目的 |\n|---|---|---|\n| `Variation.SAMPLE` | 全フィールドにサンプル値（\"sample\", `1`, enum 第一定数 等） | 通常経路のバインディング検査 |\n| `Variation.NULL` | 全フィールド `null` の object (`@JsonValue` 型は top-level `null`) | null 受容性の検査 |\n| `Variation.EMPTY` | 空/ゼロ値 (String→`\"\"`, コレクション→`[]`, primitive→デフォルト値, ネスト object は再帰的に empty) | 空値・初期値の受容性検査 |\n\n##### SAMPLEで生成される値\n\n`VariationEngine` が `JavaType` から再帰的に `JsonNode` を構築する。\n\n- スカラー:\n    - `String→\"sample\"`\n    - 数値→`1`\n    - `enum`→第一定数\n    - `LocalDate→\"2024-01-01\"`\n    - `UUID`/`Instant`/`URI` 等\n- コレクション・配列: 要素 1 個\n- Map: 1 エントリ (キー型に応じた値、例えば `Map\u003cPriority,Long\u003e` → `{\"LOW\": 1}`)\n- Bean / record: `BeanDescription` の serializationConfig 由来 property を全て埋める\n- 循環参照: `path` Set で検出して `NullNode`\n\n#### バリエーションのカスタマイズ\n\n`EngineVariation` のメソッドで既存バリエーションをベースに派生させることができます。\n\n##### 型ごとの値の上書き (`customMapping`)\n\n特定の型に対して固定値または動的な値を指定したバリエーションを作成します。\n\n```java\n// UUID 型だけ固定値を指定\nVariation.SAMPLE.customMapping(configure -\u003e configure.type(UUID.class, \"11111111-1111-1111-1111-111111111111\"))\n\n// Supplier で動的に値を生成（テスト実行ごとに異なる UUID など）\nVariation.SAMPLE.customMapping(configure -\u003e configure.type(UUID.class, UUID::randomUUID))\n```\n\n##### 名前を変えた派生バリエーション\n\nバリエーションの名前は一意である必要があります。\nSAMPLEをベースにした複数のバリエーションを作成する場合は次のようにします。\n\n```java\n\n// 名前 + カスタム値を同時に指定\nVariation.SAMPLE.customMapping(\"scenario-b\", configure -\u003e configure.type(String.class, \"custom\"))\n```\n\n各バリエーションの `name()` がファイル名（`{name}.json`）に使われます。\n\n#### 応用: 型ごとのバリエーション指定\n\n`variations(PayloadTestContext)` を override し、各ペイロードに対して実行するバリエーション群を返す。型ごとに自由に組み替え可能 (NULL を受け付けない型はリストから外す、特定型だけカスタムバリエーションを追加する、等)。\n\n```java\n@SpringBootTest\nclass JsonBindingContractTest extends JsonBindingContractTestBase {\n    \n    @Override\n    protected List\u003cVariation\u003e variations(PayloadTestContext payloadTestContext) {\n        Class\u003c?\u003e raw = ctx.rawClass();\n        // ペイロードによって使用するバリエーションを変える\n        if (payloadTestContext == SearchResult.class || raw == TodoStats.class) {\n            return List.of(Variation.SAMPLE, Variation.EMPTY);\n        }\n        // カスタマイズしたバリエーションを追加することもできる\n        if (raw == TodoList.class) {\n            return List.of(\n                    Variation.SAMPLE,\n                    Variation.SAMPLE.customMapping(\"scenario-hoge\", configure -\u003e configure\n                            .type(String.class, \"hoge\")\n                            .type(TodoList.Priority.class, \"MEDIUM\")),\n                    Variation.NULL,\n                    Variation.EMPTY\n            );\n        }\n        return super.variations(ctx);\n    }\n}\n```\n\n### fixture JSON\n\n変換するJSON（fixture JSON）は固定と動的生成に対応しており、各型とバリエーションの組み合わせで `src/test/resources/json-binding/{FQN}/{variation}.json` の有無で動作が自動切替されます。\n\n- **ファイルあり**: そのファイルの JSON を source として読み込みラウンドトリップ検査\n- **ファイルなし**: エンジンでその場生成してラウンドトリップ検査\n\nファイルがなくても「とりあえずJSONと変換できる」は確認できます。簡易確認用です。\n\nファイルを用意しておくことで「想定するJSONと変換できる」が確認できます。こちらが本命の使い方です。ライブラリの設定変更などの影響を見れます。\n\nfixture を新規作成・更新したい時は `writeMissingFiles` メソッドのオーバーライドか、 `-Djson.binding.write=true` で実行すると、ファイルが無くて build したケースのみ JSON が該当パスに書き出されれます。\nファイルがある場合はbuildされないので上書きはされません。\n\nfixture を再生成したい場合はファイルを削除してから write 実行してください。\n\n```\n# 不足分の fixture を生成\n./gradlew test -Djson.binding.write=true\n\n# 全 fixture を再生成\nrm -r src/test/resources/json-binding \u0026\u0026 ./gradlew test -Djson.binding.write=true\n```\n\n## その他\n\n### 実行時ログ\n\n各テストで使用された JSON が INFO ログに pretty-print 出力される (build か file かの出自も併記)。\n\n```\n[sample] com.example.demo.todo.TodoList (built)\n{\n  \"id\" : \"sample\",\n  \"title\" : \"sample\",\n  ...\n}\n```\n\nSLF4Jを使用したログを出力します。\n\n### 検査対象の絞り込み\n\n- ルート (= `@RequestBody` 引数 / 戻り値) のみを検査対象とし、Bean プロパティを通じた推移的な型は個別検査しない。\n- ルートのサンプル生成時点で内部型は値が埋まりラウンドトリップされるため、別途検査の必要はない。\n- `Collection\u003cT\u003e` / `Optional\u003cT\u003e` / `ResponseEntity\u003cT\u003e` 等のコンテナはアンラップして `T` を検査対象とする。\n- `java.*` のスカラー / プリミティブ / enum は検査しない。\n\n### 利用例\n\n- `:todo-app:share`: Boot 3/4 共通のアプリ本体とテスト資産\n- `:todo-app:boot3`: Spring Boot 3.5.x 用の実行モジュール\n- `:todo-app:boot4`: Spring Boot 4.0.x 用の実行モジュール\n\n互換性確認:\n\n```sh\n# ライブラリ単体\n./gradlew :spring-payload-binding-test:test\n\n# Spring Boot 3.5.x サンプル\n./gradlew :todo-app:boot3:test\n\n# Spring Boot 4.0.x サンプル\n./gradlew :todo-app:boot4:test\n\n# 3/4 サンプルをまとめて実行\n./gradlew testBootCompatibility\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Firof%2Fspring-payload-binding-test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Firof%2Fspring-payload-binding-test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Firof%2Fspring-payload-binding-test/lists"}