unit-test-json-serialization

Unit Testing JSON Serialization with @JsonTest

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "unit-test-json-serialization" with this command: npx skills add giuseppe-trisciuoglio/developer-kit/giuseppe-trisciuoglio-developer-kit-unit-test-json-serialization

Unit Testing JSON Serialization with @JsonTest

Overview

This skill provides patterns for unit testing JSON serialization and deserialization using Spring's @JsonTest and Jackson. It covers testing POJO mapping, custom serializers/deserializers, field name mappings, nested objects, date/time formatting, polymorphic types, and null handling without full Spring context.

When to Use

Use this skill when:

  • Testing JSON serialization of DTOs

  • Testing JSON deserialization to objects

  • Testing custom Jackson serializers/deserializers

  • Verifying JSON field names and formats

  • Testing null handling in JSON

  • Want fast JSON mapping tests without full Spring context

Instructions

  • Use @JsonTest annotation: Configure test context with @JsonTest for JacksonTester auto-configuration

  • Test both serialization and deserialization: Verify objects serialize to JSON and JSON deserializes to objects

  • Use JacksonTester: Autowire JacksonTester for type-safe JSON assertions

  • Test null handling: Verify null fields are handled correctly (included or excluded)

  • Test nested objects: Verify complex nested structures serialize/deserialize properly

  • Test date/time formats: Verify LocalDateTime, Date, and other temporal types format correctly

  • Test custom serializers: Verify @JsonSerialize and custom JsonSerializer implementations work

  • Use JsonPath assertions: Extract and verify specific JSON paths with JsonPath matchers

Examples

Setup: JSON Testing

Maven

<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> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency>

Gradle

dependencies { implementation("org.springframework.boot:spring-boot-starter-json") implementation("com.fasterxml.jackson.core:jackson-databind") testImplementation("org.springframework.boot:spring-boot-starter-test") }

Basic Pattern: @JsonTest

Test JSON Serialization

import org.springframework.boot.test.autoconfigure.json.JsonTest; import org.springframework.boot.test.json.JacksonTester; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*;

@JsonTest class UserDtoJsonTest {

@Autowired private JacksonTester<UserDto> json;

@Test void shouldSerializeUserToJson() throws Exception { UserDto user = new UserDto(1L, "Alice", "alice@example.com", 25);

org.assertj.core.data.Offset result = json.write(user);

result
  .extractingJsonPathNumberValue("$.id").isEqualTo(1)
  .extractingJsonPathStringValue("$.name").isEqualTo("Alice")
  .extractingJsonPathStringValue("$.email").isEqualTo("alice@example.com")
  .extractingJsonPathNumberValue("$.age").isEqualTo(25);

}

@Test void shouldDeserializeJsonToUser() throws Exception { String json_content = "{"id":1,"name":"Alice","email":"alice@example.com","age":25}";

UserDto user = json.parse(json_content).getObject();

assertThat(user)
  .isNotNull()
  .hasFieldOrPropertyWithValue("id", 1L)
  .hasFieldOrPropertyWithValue("name", "Alice")
  .hasFieldOrPropertyWithValue("email", "alice@example.com")
  .hasFieldOrPropertyWithValue("age", 25);

}

@Test void shouldHandleNullFields() throws Exception { String json_content = "{"id":1,"name":null,"email":"alice@example.com","age":null}";

UserDto user = json.parse(json_content).getObject();

assertThat(user.getName()).isNull();
assertThat(user.getAge()).isNull();

} }

Testing Custom JSON Properties

@JsonProperty and @JsonIgnore

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

@JsonProperty("total_amount") private BigDecimal amount;

@JsonIgnore private String internalNote;

private LocalDateTime createdAt; }

@JsonTest class OrderJsonTest {

@Autowired private JacksonTester<Order> json;

@Test void shouldMapJsonPropertyNames() throws Exception { String json_content = "{"order_id":123,"total_amount":99.99,"createdAt":"2024-01-15T10:30:00"}";

Order order = json.parse(json_content).getObject();

assertThat(order.getId()).isEqualTo(123L);
assertThat(order.getAmount()).isEqualByComparingTo(new BigDecimal("99.99"));

}

@Test void shouldIgnoreJsonIgnoreAnnotatedFields() throws Exception { Order order = new Order(123L, new BigDecimal("99.99")); order.setInternalNote("Secret note");

JsonContent&#x3C;Order> result = json.write(order);

assertThat(result.json).doesNotContain("internalNote");

} }

Testing List Deserialization

JSON Arrays

@JsonTest class UserListJsonTest {

@Autowired private JacksonTester<List<UserDto>> json;

@Test void shouldDeserializeUserList() throws Exception { String jsonArray = "[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]";

List&#x3C;UserDto> users = json.parseObject(jsonArray);

assertThat(users)
  .hasSize(2)
  .extracting(UserDto::getName)
  .containsExactly("Alice", "Bob");

}

@Test void shouldSerializeUserListToJson() throws Exception { List<UserDto> users = List.of( new UserDto(1L, "Alice"), new UserDto(2L, "Bob") );

JsonContent&#x3C;List&#x3C;UserDto>> result = json.write(users);

result.json.contains("Alice").contains("Bob");

} }

Testing Nested Objects

Complex JSON Structures

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

public class Category { private Long id; private String name; }

public class Review { private String reviewer; private int rating; private String comment; }

@JsonTest class ProductJsonTest {

@Autowired private JacksonTester<Product> json;

@Test void shouldSerializeNestedObjects() throws Exception { Category category = new Category(1L, "Electronics"); Product product = new Product(1L, "Laptop", category);

JsonContent&#x3C;Product> result = json.write(product);

result
  .extractingJsonPathNumberValue("$.id").isEqualTo(1)
  .extractingJsonPathStringValue("$.name").isEqualTo("Laptop")
  .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())
  .isNotNull()
  .hasFieldOrPropertyWithValue("name", "Electronics");

}

@Test void shouldHandleListOfNestedObjects() throws Exception { String json_content = "{"id":1,"name":"Laptop","reviews":[{"reviewer":"John","rating":5},{"reviewer":"Jane","rating":4}]}";

Product product = json.parse(json_content).getObject();

assertThat(product.getReviews())
  .hasSize(2)
  .extracting(Review::getRating)
  .containsExactly(5, 4);

} }

Testing Date/Time Formatting

LocalDateTime and Other Temporal Types

@JsonTest class DateTimeJsonTest {

@Autowired private JacksonTester<Event> json;

@Test void shouldFormatDateTimeCorrectly() throws Exception { LocalDateTime dateTime = LocalDateTime.of(2024, 1, 15, 10, 30, 0); Event event = new Event("Conference", dateTime);

JsonContent&#x3C;Event> result = json.write(event);

result.extractingJsonPathStringValue("$.scheduledAt").isEqualTo("2024-01-15T10:30:00");

}

@Test void shouldDeserializeDateTimeFromJson() throws Exception { String json_content = "{"name":"Conference","scheduledAt":"2024-01-15T10:30:00"}";

Event event = json.parse(json_content).getObject();

assertThat(event.getScheduledAt())
  .isEqualTo(LocalDateTime.of(2024, 1, 15, 10, 30, 0));

} }

Testing Custom Serializers

Custom JsonSerializer Implementation

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

public class Price { @JsonSerialize(using = CustomMoneySerializer.class) private BigDecimal amount; }

@JsonTest class CustomSerializerTest {

@Autowired private JacksonTester<Price> json;

@Test void shouldUseCustomSerializer() throws Exception { Price price = new Price(new BigDecimal("99.99"));

JsonContent&#x3C;Price> result = json.write(price);

result.extractingJsonPathStringValue("$.amount").isEqualTo("$99.99");

} }

Testing Polymorphic Deserialization

Type Information in JSON

@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 { private String id; }

@JsonTest class PolymorphicJsonTest {

@Autowired private JacksonTester<PaymentMethod> json;

@Test void shouldDeserializeCreditCard() throws Exception { String json_content = "{"type":"credit_card","id":"card123","cardNumber":"****1234"}";

PaymentMethod method = json.parse(json_content).getObject();

assertThat(method).isInstanceOf(CreditCard.class);

}

@Test void shouldDeserializePayPal() throws Exception { String json_content = "{"type":"paypal","id":"pp123","email":"user@paypal.com"}";

PaymentMethod method = json.parse(json_content).getObject();

assertThat(method).isInstanceOf(PayPal.class);

} }

Best Practices

  • Use @JsonTest for focused JSON testing

  • Test both serialization and deserialization

  • Test null handling and missing fields

  • Test nested and complex structures

  • Verify field name mapping with @JsonProperty

  • Test date/time formatting thoroughly

  • Test edge cases (empty strings, empty collections)

Common Pitfalls

  • Not testing null values

  • Not testing nested objects

  • Forgetting to test field name mappings

  • Not verifying JSON property presence/absence

  • Not testing deserialization of invalid JSON

Constraints and Warnings

  • @JsonTest loads limited context: Only JSON-related beans are available; use @SpringBootTest for full context

  • Jackson version compatibility: Ensure Jackson annotations match the Jackson version in use

  • Date format standards: ISO-8601 is the default format; custom formats require @JsonFormat annotation

  • Null handling: Use JsonInclude.Include.NON_NULL to exclude null fields from serialization

  • Circular references: Be aware of circular references; use @JsonManagedReference/@JsonBackReference

  • Polymorphic type handling: @JsonTypeInfo must be correctly configured for polymorphic deserialization

  • Immutable objects: Use @JsonCreator and @JsonProperty for constructor-based deserialization

Troubleshooting

JacksonTester not available: Ensure class is annotated with @JsonTest .

Field name doesn't match: Check @JsonProperty annotation and Jackson configuration.

DateTime parsing fails: Verify date format matches Jackson's expected format.

References

  • Spring @JsonTest Documentation

  • Jackson ObjectMapper

  • JSON Annotations

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

shadcn-ui

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

tailwind-css-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

unit-test-bean-validation

No summary provided by upstream source.

Repository SourceNeeds Review