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