REST Assured interview question
How do you handle serialization and deserialization with POJOs in REST Assured?
Short answer
Pass a POJO or Java record to body() and REST Assured serializes it with Jackson (or Gson) found on the classpath; use extract().as(Type.class) or a TypeRef for generics to deserialize. Control mapping with Jackson annotations and decide deliberately whether unknown fields should fail tests.
Answer
Serialization: object to JSON
Pass an object to body(). REST Assured picks a mapper from the classpath (Jackson 2 first, then Gson, then JSON-B), so you need jackson-databind as a dependency. The content type must be JSON, otherwise REST Assured does not know how to serialize.
public record CreateOrder(String customerId, List<Line> lines, String currency) {
public record Line(String sku, int qty) {}
}
CreateOrder request = new CreateOrder("c-1", List.of(new CreateOrder.Line("SKU-1", 2)), "EUR");
OrderResponse created = given()
.contentType(ContentType.JSON)
.body(request)
.when()
.post("/orders")
.then()
.statusCode(201)
.extract().as(OrderResponse.class);
assertThat(created.lines()).hasSize(1);
assertThat(created.currency()).isEqualTo("EUR");
Java records work with Jackson 2.12 and later, which keeps test models short. Builders (Lombok @Builder or a hand-written builder) help when a payload has many optional fields and tests vary only a few.
Deserialization: JSON to object
extract().as(OrderResponse.class)for a single object.- For generics, type erasure means
List.classloses the element type. UseTypeRef:
List<OrderResponse> orders = given().spec(spec)
.when().get("/orders")
.then().statusCode(200)
.extract().as(new TypeRef<List<OrderResponse>>() {});
jsonPath().getList("items", OrderResponse.class)deserializes a nested list.
Mapping control with Jackson annotations
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public record OrderResponse(
String id,
@JsonProperty("customer_id") String customerId,
String status,
List<Line> lines,
String currency,
BigDecimal total) {
public record Line(String sku, int qty) {}
}
@JsonPropertymaps snake_case API fields to Java names (or configure aPropertyNamingStrategies.SNAKE_CASEmapper globally).@JsonInclude(NON_NULL)stops nulls being sent, which matters when “field absent” and “field null” mean different things to the API.BigDecimalfor money avoids float rounding issues.
A custom ObjectMapper (for example with JavaTimeModule for dates) is registered through RestAssuredConfig and ObjectMapperConfig.jackson2ObjectMapperFactory(...).
The design decision: unknown fields
- Lenient (
ignoreUnknown = true): tests do not break when the API adds a field. Good for most functional tests. - Strict (Jackson’s default,
FAIL_ON_UNKNOWN_PROPERTIES): catches unexpected additions, which can matter for data exposure, but is brittle.
I keep models lenient and use JSON Schema validation to check the contract explicitly, so each concern has one owner.
Where POJOs do not fit
Negative tests often need payloads a POJO cannot express: wrong types ("qty": "two"), malformed JSON, duplicate keys, missing required fields that the record would set to null. For those, send a raw string or a Map<String, Object>.
What interviewers look for
- Knowing a JSON mapper must be on the classpath and how body() and as() use it.
- TypeRef for generic lists.
- Practical Jackson annotations and BigDecimal for money.
- A reasoned stance on unknown fields, and using raw payloads for negative tests.
A strong answer covers
- Serialization: passing an object to body() with a JSON content type
- Needs Jackson, Gson or JSON-B on the classpath
- Deserialization with as() and TypeRef for generic collections
- Annotations for mapping: @JsonProperty, @JsonIgnoreProperties, @JsonInclude
- Trade-off on unknown fields and when to use schema validation or raw JSON for negative tests