Intermediate
Chapter 10 · 10 min read
Request & Response Serialization
Serialize request bodies from objects and deserialize responses into typed models (POJOs, classes, dataclasses) for cleaner, type-safe tests.
Serialization & Deserialization
Instead of working with raw JSON strings, professional API tests use typed objects. Serialization converts objects to JSON for request bodies. Deserialization converts JSON responses back into objects for type-safe assertions.
Benefits
Type-safe code catches errors at compile time (Java) or with IDE support. It provides autocompletion, makes assertions cleaner, and keeps test code readable. It also serves as living documentation of the API structure.
serialization.test.js
// JavaScript approach: create model classes/factories
class Post {
constructor({ id, userId, title, body }) {
this.id = id;
this.userId = userId;
this.title = title;
this.body = body;
}
static fromJson(json) {
return new Post(json);
}
toJson() {
const obj = { userId: this.userId, title: this.title, body: this.body };
if (this.id) obj.id = this.id;
return obj;
}
validate() {
console.assert(typeof this.id === 'number', 'id should be number');
console.assert(typeof this.title === 'string', 'title should be string');
console.assert(this.title.length > 0, 'title should not be empty');
return true;
}
}
// Factory for creating test data
class PostFactory {
static create(overrides = {}) {
return new Post({
userId: 1,
title: 'Default Test Title',
body: 'Default test body content',
...overrides
});
}
}
// Deserialize response into model
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const post = Post.fromJson(await resp.json());
post.validate();
console.log(`Post: "${post.title}" by user ${post.userId}`);
// Serialize model for request
const newPost = PostFactory.create({ title: 'My Custom Post' });
const createResp = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newPost.toJson())
});
const created = Post.fromJson(await createResp.json());
console.assert(created.title === 'My Custom Post');
console.log('Serialization tests passed!');
Post.java
// Model POJO — src/test/java/com/apitesting/models/Post.java
package com.apitesting.models;
public class Post {
private int id;
private int userId;
private String title;
private String body;
// Default constructor (required for deserialization)
public Post() {}
// Builder-style constructor
public Post(int userId, String title, String body) {
this.userId = userId;
this.title = title;
this.body = body;
}
// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public int getUserId() { return userId; }
public void setUserId(int userId) { this.userId = userId; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getBody() { return body; }
public void setBody(String body) { this.body = body; }
}
SerializationTest.java
import static io.restassured.RestAssured.*;
import com.apitesting.models.Post;
import org.testng.annotations.Test;
import org.testng.Assert;
public class SerializationTest {
private static final String BASE = "https://jsonplaceholder.typicode.com";
@Test
public void testDeserialization() {
// RestAssured auto-deserializes with .as(Class)
Post post = given().baseUri(BASE)
.when().get("/posts/1")
.then().statusCode(200)
.extract().as(Post.class);
Assert.assertEquals(post.getId(), 1);
Assert.assertEquals(post.getUserId(), 1);
Assert.assertNotNull(post.getTitle());
Assert.assertFalse(post.getTitle().isEmpty());
}
@Test
public void testSerialization() {
// Create POJO and send as request body
Post newPost = new Post(1, "RestAssured Post", "Created via POJO");
Post created = given().baseUri(BASE)
.contentType("application/json")
.body(newPost) // Auto-serialized to JSON
.when()
.post("/posts")
.then()
.statusCode(201)
.extract().as(Post.class);
Assert.assertEquals(created.getTitle(), "RestAssured Post");
Assert.assertTrue(created.getId() > 0);
}
@Test
public void testDeserializeList() {
// Deserialize array of objects
Post[] posts = given().baseUri(BASE)
.when().get("/posts")
.then().statusCode(200)
.extract().as(Post[].class);
Assert.assertEquals(posts.length, 100);
Assert.assertEquals(posts[0].getId(), 1);
}
}
test_serialization.py
import requests
from dataclasses import dataclass, asdict, field
from typing import Optional, List
BASE_URL = 'https://jsonplaceholder.typicode.com'
@dataclass
class Post:
userId: int
title: str
body: str
id: Optional[int] = None
@classmethod
def from_dict(cls, data: dict) -> 'Post':
return cls(
id=data.get('id'),
userId=data['userId'],
title=data['title'],
body=data['body']
)
def to_dict(self) -> dict:
d = asdict(self)
if self.id is None:
del d['id']
return d
def validate(self):
assert isinstance(self.userId, int)
assert isinstance(self.title, str)
assert len(self.title) > 0
# Factory for test data
def create_post(**overrides) -> Post:
defaults = {
'userId': 1,
'title': 'Default Test Title',
'body': 'Default test body content'
}
defaults.update(overrides)
return Post(**defaults)
def test_deserialization():
"""Deserialize response into dataclass"""
response = requests.get(f'{BASE_URL}/posts/1')
post = Post.from_dict(response.json())
post.validate()
assert post.id == 1
assert post.userId == 1
print(f'Post: "{post.title}" by user {post.userId}')
def test_serialization():
"""Serialize dataclass for request"""
new_post = create_post(title='Python Requests Post')
response = requests.post(
f'{BASE_URL}/posts',
json=new_post.to_dict()
)
created = Post.from_dict(response.json())
assert created.title == 'Python Requests Post'
def test_deserialize_list():
"""Deserialize list of objects"""
response = requests.get(f'{BASE_URL}/posts')
posts = [Post.from_dict(p) for p in response.json()]
assert len(posts) == 100
assert posts[0].id == 1
for post in posts:
post.validate()
API Testing
Intermediate
Request & Response Serialization
Written by PV
© 2026 All Rights Reserved