К основному содержанию
скилл CX Codex

unit-test-json-serialization — скилл для Claude Code: тесты JSON

Скилл подсказывает, как писать юнит-тесты сериализации и десериализации JSON на Jackson и @JsonTest в Spring Boot.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-json-serialization -a claude-code
Открыть на GitHub

Что делает unit-test-json-serialization

Скилл даёт готовые шаблоны тестов: маппинг POJO, кастомные сериализаторы, переименование полей через @JsonProperty, @JsonIgnore, вложенные объекты, списки, форматы даты и времени, полиморфные типы. Работает через JacksonTester — типизированные проверки JSON по путям extractingJsonPath* и разбор строки в объект.

Порядок шагов простой: пометить класс теста @JsonTest, внедрить JacksonTester для нужного типа, проверить запись объекта в JSON, затем разбор обратно, а после — крайние случаи вроде null, пустых коллекций и отсутствующих полей. В конце скилл советует убедиться, что тест падает осмысленно на неверных данных.

Для работы нужны зависимости spring-boot-starter-json и spring-boot-starter-test — в README приведены блоки для Maven и Gradle. Скилл рассчитан на Spring Boot, поэтому в проекте без Spring аннотация @JsonTest и автоконфигурация JacksonTester не появятся, и примеры придётся переписывать на голый Jackson.

Какие задачи решает

  • проверить, что DTO отдаёт нужные имена полей в JSON
  • поймать регрессию в кастомном сериализаторе денег
  • убедиться, что @JsonIgnore скрывает служебное поле
  • разобрать полиморфный JSON в нужный подкласс
  • проверить формат LocalDateTime в ответе

Примеры запросов агенту

  • Напиши @JsonTest для UserDto и проверь сериализацию через JacksonTester
  • Добавь тест на десериализацию JSON с order_id и total_amount в Order
  • Проверь, что кастомный сериализатор выводит сумму как $99.99

Как установить unit-test-json-serialization

1Через Skills CLI (Vercel)

npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-json-serialization -a claude-code

2Вручную: положить файлы

~/.claude/skills/unit-test-json-serialization/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/unit-test-json-serialization/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о unit-test-json-serialization

Как установить unit-test-json-serialization в Claude?

Выполните npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-json-serialization -a claude-code. Файл скилла окажется по пути ~/.claude/skills/unit-test-json-serialization/SKILL.md.

Что делает unit-test-json-serialization?

Собирает паттерны юнит-тестов JSON на Jackson и @JsonTest: маппинг DTO, кастомные сериализаторы, вложенные объекты, даты и полиморфные типы.

Что нужно для работы скилла?

Зависимости spring-boot-starter-json и spring-boot-starter-test. Примеры даны для Maven и Gradle.

Исходный текст

SKILL.md · MIT
---
name: unit-test-json-serialization
description: Provides patterns for unit testing JSON serialization/deserialization with Jackson and `@JsonTest`. Validates JSON mapping, custom serializers, date formats, and polymorphic types. Use when testing JSON serialization, validating custom serializers, or writing JSON unit tests in Spring Boot applications.
allowed-tools: Read, Write, Bash, Glob, Grep
---

# Unit Testing JSON Serialization with `@JsonTest`

## Overview

Provides patterns for unit testing JSON serialization and deserialization using Spring's `@JsonTest` and Jackson. Covers POJO mapping, custom serializers, field name mappings, nested objects, date/time formatting, and polymorphic types.

## When to Use

- Testing JSON serialization/deserialization of DTOs
- Verifying custom Jackson serializers/deserializers
- Validating `@JsonProperty`, `@JsonIgnore`, and field name mappings
- Testing date/time format handling (LocalDateTime, Date)
- Testing null handling and missing fields
- Testing polymorphic type deserialization

## Instructions

1. **Annotate test class with `@JsonTest`** → Enables JacksonTester auto-configuration
2. **Autowire JacksonTester for target type** → Provides type-safe JSON assertions
3. **Test serialization** → Call `json.write(object)` and assert JSON paths with `extractingJsonPath*`
4. **Test deserialization** → Call `json.parse(json)` or `json.parseObject(json)` and assert object state
5. **Validate round-trip** → Serialize, then deserialize, verify same data (if object is properly comparable)
6. **Test edge cases** → Null values, missing fields, empty collections, invalid JSON
7. **Add validation checkpoints**: After each assertion, verify the test fails meaningfully with wrong data

## Examples

### Maven Setup
```xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>
```

### Gradle Setup
```kotlin
dependencies {
  implementation("org.springframework.boot:spring-boot-starter-json")
  testImplementation("org.springframework.boot:spring-boot-starter-test")
}
```

### Basic Serialization and Deserialization

```java
@JsonTest
class UserDtoJsonTest {

  @Autowired
  private JacksonTester<UserDto> json;

  @Test
  void shouldSerializeUserToJson() throws Exception {
    UserDto user = new UserDto(1L, "Alice", "[email protected]", 25);
    JsonContent<UserDto> result = json.write(user);

    result
      .extractingJsonPathNumberValue("$.id").isEqualTo(1)
      .extractingJsonPathStringValue("$.name").isEqualTo("Alice")
      .extractingJsonPathStringValue("$.email").isEqualTo("[email protected]")
      .extractingJsonPathNumberValue("$.age").isEqualTo(25);
  }

  @Test
  void shouldDeserializeJsonToUser() throws Exception {
    String json_content = "{\"id\":1,\"name\":\"Alice\",\"email\":\"[email protected]\",\"age\":25}";
    UserDto user = json.parse(json_content).getObject();

    assertThat(user.getId()).isEqualTo(1L);
    assertThat(user.getName()).isEqualTo("Alice");
    assertThat(user.getEmail()).isEqualTo("[email protected]");
    assertThat(user.getAge()).isEqualTo(25);
  }

  @Test
  void shouldHandleNullFields() throws Exception {
    String json_content = "{\"id\":1,\"name\":null,\"email\":\"[email protected]\"}";
    UserDto user = json.parse(json_content).getObject();
    assertThat(user.getName()).isNull();
  }
}
```

### Custom JSON Properties

```java
public class Order {
  @JsonProperty("order_id")
  private Long id;

  @JsonProperty("total_amount")
  private BigDecimal amount;

  @JsonIgnore
  private String internalNote;
}

@JsonTest
class OrderJsonTest {

  @Autowired
  private JacksonTester<Order> json;

  @Test
  void shouldMapJsonPropertyNames() throws Exception {
    String json_content = "{\"order_id\":123,\"total_amount\":99.99}";
    Order order = json.parse(json_content).getObject();
    assertThat(order.getId()).isEqualTo(123L);
    assertThat(order.getAmount()).isEqualByComparingTo(new BigDecimal("99.99"));
  }

  @Test
  void shouldIgnoreJsonIgnoreFields() throws Exception {
    Order order = new Order(123L, new BigDecimal("99.99"));
    order.setInternalNote("Secret");
    assertThat(json.write(order).json).doesNotContain("internalNote");
  }
}
```

### Nested Objects

```java
public class Product {
  private Long id;
  private String name;
  private Category category;
  private List<Review> reviews;
}

@JsonTest
class ProductJsonTest {

  @Autowired
  private JacksonTester<Product> json;

  @Test
  void shouldSerializeNestedObjects() throws Exception {
    Product product = new Product(1L, "Laptop", new Category(1L, "Electronics"));
    JsonContent<Product> result = json.write(product);

    result
      .extractingJsonPathNumberValue("$.category.id").isEqualTo(1)
      .extractingJsonPathStringValue("$.category.name").isEqualTo("Electronics");
  }

  @Test
  void shouldDeserializeNestedObjects() throws Exception {
    String json_content = "{\"id\":1,\"name\":\"Laptop\",\"category\":{\"id\":1,\"name\":\"Electronics\"}}";
    Product product = json.parse(json_content).getObject();
    assertThat(product.getCategory().getName()).isEqualTo("Electronics");
  }

  @Test
  void shouldHandleListOfNestedObjects() throws Exception {
    String json_content = "{\"id\":1,\"reviews\":[{\"rating\":5},{\"rating\":4}]}";
    Product product = json.parse(json_content).getObject();
    assertThat(product.getReviews()).hasSize(2);
  }
}
```

### Date/Time Formatting

```java
@JsonTest
class DateTimeJsonTest {

  @Autowired
  private JacksonTester<Event> json;

  @Test
  void shouldFormatDateTimeCorrectly() throws Exception {
    LocalDateTime dt = LocalDateTime.of(2024, 1, 15, 10, 30, 0);
    json.write(new Event("Conference", dt))
      .extractingJsonPathStringValue("$.scheduledAt").isEqualTo("2024-01-15T10:30:00");
  }
}
```

### Custom Serializers

```java
public class CustomMoneySerializer extends JsonSerializer<BigDecimal> {
  @Override
  public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    gen.writeString(value == null ? null : String.format("$%.2f", value));
  }
}

@JsonTest
class CustomSerializerTest {

  @Autowired
  private JacksonTester<Price> json;

  @Test
  void shouldUseCustomSerializer() throws Exception {
    json.write(new Price(new BigDecimal("99.99")))
      .extractingJsonPathStringValue("$.amount").isEqualTo("$99.99");
  }
}
```

### Polymorphic Deserialization

```java
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
  @JsonSubTypes.Type(value = CreditCard.class, name = "credit_card"),
  @JsonSubTypes.Type(value = PayPal.class, name = "paypal")
})
public abstract class PaymentMethod { }

@JsonTest
class PolymorphicJsonTest {

  @Autowired
  private JacksonTester<PaymentMethod> json;

  @Test
  void shouldDeserializeCreditCard() throws Exception {
    String json_content = "{\"type\":\"credit_card\",\"id\":\"card123\"}";
    assertThat(json.parse(json_content).getObject()).isInstanceOf(CreditCard.class);
  }

  @Test
  void shouldDeserializePayPal() throws Exception {
    String json_content = "{\"type\":\"paypal\",\"id\":\"pp123\"}";
    assertThat(json.parse(json_content).getObject()).isInstanceOf(PayPal.class);
  }
}
```

## Best Practices

- Test serialization AND deserialization for complete coverage
- Verify JSON paths individually rather than comparing full JSON strings
- Test null handling explicitly — null fields may be included or excluded depending on `@JsonInclude`
- Use `extractingJsonPath*` methods for precise field assertions
- Test round-trip: serialize an object, deserialize the JSON, verify the result matches
- Validate edge cases: empty strings, empty collections, deeply nested structures
- Group related assertions in a single test for clarity

## Constraints and Warnings

- **`@JsonTest` loads limited context**: Only JSON-related beans; use `@SpringBootTest` for full Spring context
- **Jackson version**: Ensure annotation versions match the Jackson version in use
- **Date formats**: ISO-8601 is default; use `@JsonFormat` for custom patterns
- **Null handling**: Use `@JsonInclude(Include.NON_NULL)` to exclude nulls from serialization
- **Circular references**: Use `@JsonManagedReference`/`@JsonBackReference` to prevent infinite loops
- **Immutable objects**: Use `@JsonCreator` + `@JsonProperty` for constructor-based deserialization
- **Polymorphic types**: `@JsonTypeInfo` must correctly identify the subtype for deserialization to work

## Debugging Workflow

When a JSON test fails, follow this workflow:

| Failure Symptom | Common Cause | How to Verify |
|----------------|--------------|---------------|
| `JsonPath` assertion fails | Field name mismatch | Check `@JsonProperty` spelling matches JSON key |
| Null expected but got value | `@JsonInclude(NON_NULL)` configured | Verify annotation on field/class |
| Deserialization returns wrong type | Missing `@JsonTypeInfo` | Add type info property to JSON or configure subtype mapping |
| Date format mismatch | Format string incorrect | Confirm `@JsonFormat(pattern=...)` matches expected string |
| Missing field in output | `@JsonIgnore` or transient modifier | Check field for `@JsonIgnore` or `transient` keyword |
| Nested object is null | Inner JSON missing or malformed | Log parsed JSON; verify inner structure matches POJO |
| `JsonParseException` | Malformed JSON string | Validate JSON syntax; check for unescaped characters |

**Validation checkpoint after fixing**: Re-run the test — if it passes, write a complementary test for the opposite case (e.g., if you fixed null handling, add a test for non-null values to prevent regression).

## References

- [Spring `@JsonTest` Documentation](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/json/JsonTest.html)
- [Jackson ObjectMapper](https://fasterxml.github.io/jackson-databind/javadoc/2.15/com/fasterxml/jackson/databind/ObjectMapper.html)
- [Jackson Annotations](https://fasterxml.github.io/jackson-annotations/javadoc/2.15/)

© giuseppe-trisciuoglio · MIT · giuseppe-trisciuoglio/developer-kit

Похожие скиллы

Всё по теме «Тесты и QA» →
flutter-implement-json-serialization flutter Скилл учит агента создавать Dart-модели с методами fromJson и toJson через библиотеку dart:convert. Пригодится при ручном разборе простых структур данных в Flutter. CX Codex +1 37 400установок 321-frameworks-spring-boot-testing-unit-tests Juan Antonio Breña Moral Скилл ведёт агента по юнит-тестам Spring Boot: Mockito без поднятия контекста, срезы для контроллеров, параметризованные тесты. Пригодится, когда в проекте расплодились @SpringBootTest. CX Codex +1 387установок 131-java-testing-unit-testing Juan Antonio Breña Moral Скилл помогает писать и рефакторить модульные тесты на Java по стандартам JUnit 5, AssertJ и Mockito. Он находит антипаттерны вроде общего состояния и проверяет граничные условия. CX Codex +1 449установок Python Unit Test Generator — Comprehensive, Coverage-Mapped & Production-Ready sivasaiyadav8143 Промпт превращает кусок Python-кода в готовый набор юнит-тестов на pytest с картой покрытия и моками внешних зависимостей. GP ChatGPT ★ 174 597звёзд репо typescript-unit-testing bmad-labs Скилл для юнит-тестов TypeScript и NestJS: Jest, моки через @golevelup/ts-jest и структура AAA. Пригодится, когда пишете или чините .spec.ts. CX Codex +1 496установок serialization Aaronontheweb Помогает агенту выбрать формат сериализации для .NET: схему вместо рефлексии, System.Text.Json с генераторами для JSON. Пригодится при проектировании API, обмена сообщениями и хранения событий. CX Codex +1 569установок

Из того же набора93

spring-boot-crud-patterns Скилл собирает полный CRUD-слой для сервисов на Spring Boot 3: агрегаты, JPA-репозитории, DTO и REST-контроллеры по feature-архитектуре. CX Codex +1 3 900установок typescript-security-review Проверяет код TypeScript и Node.js на уязвимости OWASP, утечки секретов и ошибки в настройке JWT или CORS. Пригодится перед релизом или при аудите авторизации в Express, NestJS и Next.js. CX Codex +1 3 900установок prompt-engineering Скилл помогает писать, отлаживать и проверять промпты для LLM. Пригодится для few-shot-примеров, системных инструкций и шаблонов. CX Codex +1 3 900установок spring-boot-saga-pattern Скилл для распределённых транзакций в микросервисах на Spring Boot: хореография и оркестрация, компенсирующие операции, Kafka, RabbitMQ, Axon. CX Codex +1 3 800установок unit-test-controller-layer Генерирует модульные тесты для REST-контроллеров Spring через MockMvc и Mockito. Пригодится для быстрой проверки HTTP-статусов, валидации и JsonPath без запуска полного контекста. CX Codex +1 3 800установок unit-test-parameterized Скилл для параметризованных тестов на JUnit 5: один метод гоняет много входных значений. Пригодится, когда надо покрыть граничные случаи без копипасты. CX Codex +1 3 800установок nextjs-authentication Скилл даёт готовые шаблоны аутентификации для Next.js 15+ App Router на Auth.js 5: провайдеры, защита маршрутов, сессии и роли. CX Codex +1 3 800установок spring-boot-dependency-injection Скилл внедряет паттерны внедрения зависимостей через конструктор в Spring Boot. Он помогает избавиться от field injection и настроить выбор бинов через @Primary и @Qualifier. CX Codex +1 3 800установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой