Skip to main content

Verifying responses

With Serenity/JS, you can verify responses to your API requests using LastResponse and the exact same Serenity/JS assertions module you'd use for other kinds of test scenarios.

Check the LastResponse documentation for examples on how to extract interesting information from the response:

Verifying the response status

To extract the response status, instruct the actor to Send a HTTPRequest and use LastResponse.status().

import { actorCalled } from '@serenity-js/core'
import { CallAnApi, GetRequest, LastResponse, Send } from '@serenity-js/rest'
import { Ensure, equals } from '@serenity-js/assertions'

await actorCalled('Apisitt')
.whoCan(CallAnApi.at('https://api.example.org/'))
.attemptsTo(
Send.a(GetRequest.to('/books/0-688-00230-7')),
Ensure.that(LastResponse.status(), equals(200)),
)

Verifying the response body

To extract the response body, instruct the actor to Send a HTTPRequest and use LastResponse.body<T>(). Here T is an optional generic parameter describing the shape of the response body.

import { actorCalled } from '@serenity-js/core'
import { CallAnApi, GetRequest, LastResponse, Send } from '@serenity-js/rest'
import { Ensure, equals } from '@serenity-js/assertions'

interface Book {
title: string;
author: string;
}

await actorCalled('Apisitt')
.whoCan(CallAnApi.at('https://api.example.org/'))
.attemptsTo(
Send.a(GetRequest.to('/books/0-688-00230-7')),
Ensure.that(LastResponse.status(), equals(200)),
Ensure.that(LastResponse.header('Content-Type'), equals('application/json')),
Ensure.that(LastResponse.body<Book>(), equals({
title: 'Zen and the Art of Motorcycle Maintenance: An Inquiry into Values',
author: 'Robert M. Pirsig',
})),
)

Mapping response bodies to domain models

A response body often has a transport-specific JSON shape that is not the model your test needs to work with. Use the constructor form of Question.as() to map LastResponse.body<T>() to a domain model and expose operations in your application's language.

spec/user-profile.spec.ts
import { Ensure, equals } from '@serenity-js/assertions';
import { actorCalled } from '@serenity-js/core';
import { CallAnApi, GetRequest, LastResponse, Send } from '@serenity-js/rest';

interface UserDetails {
id: string;
first_name: string;
last_name: string;
roles: string[];
}

class UserProfile {
constructor(private readonly details: UserDetails) {
}

displayName(): string {
return `${ this.details.first_name } ${ this.details.last_name }`;
}

canManageUsers(): boolean {
return this.details.roles.includes('user-admin');
}
}

const userProfile = () =>
LastResponse.body<UserDetails>().as(UserProfile);

await actorCalled('Apisitt')
.whoCan(CallAnApi.at('https://api.example.org/'))
.attemptsTo(
Send.a(GetRequest.to('/users/42')),
Ensure.that(userProfile().displayName(), equals('Alice Smith')),
Ensure.that(userProfile().canManageUsers(), equals(true)),
);

LastResponse.body<UserDetails>() remains a Question, and .as(UserProfile) defers the JSON-to-domain mapping until the Actor needs the answer. The resulting adapter exposes UserProfile operations such as displayName() and canManageUsers() as Questions, while preserving the composable QuestionAdapter API that Screenplay code relies on.