API Testing in Playwright

Anandhi K
5 min readJan 17, 2024

--

This blog is on Playwright API Testing using Typescript. I will cover the following topics,

1. Introduction to Playwright API Testing

2. What is APIRequestContext?

3. How to use various Request methods and verify its response?

Playwright can be used to get access to the REST API of your application.

Sometimes you may want to send requests to the server directly from Node.js without loading a page and running js code in it. A few examples where it may come in handy:

· Test your server API.

· Prepare server side state before visiting the web application in a test.

· Validate server side post-conditions after running some actions in the browser.

All of that could be achieved via APIRequestContext methods.

What is APIRequestContext ?

This API is used for the Web API testing and can be used to trigger API endpoints, configure micro-services, prepare environment or the service to your e2e test.

Each Playwright browser context has associated with APIRequestContext instance which shares cookie storage with the browser context and can be accessed via browserContext.request or page.request. It is also possible to create a new APIRequestContext instance manually by calling apiRequest.newContext([options]).

Playwright Test comes with the built-in request fixture that respects configuration options like baseURL or extraHTTPHeaders we specified and is ready to send some requests.

In this blog, we will use the url — Welcome to Restful-Booker

This is a restful booker API. Restful-booker is a CRUD (Create, Read, Update and Delete) web API that comes with authentication features.

We will implement the following scenarios,

a. GET :

i. Get all the records

ii. Get the booking details by using query param and assert the response

b. POST :

i. Create a booking by passing data

ii. Create a booking by passing Json file

c. PUT — Update booking attributes for an existing resource and assert the response.

d. DELETE — Finally, we will delete the booking and assert the response code.

For easy understanding, we will create a simple Playwright API testing approach:

Config file :

Create a config file with the name playwright.config.ts under project folder.

import { devices, PlaywrightTestConfig } from '@playwright/test';

const config: PlaywrightTestConfig = {
use: {
headless: false,
browserName: "chromium",
ignoreHTTPSErrors: true,
screenshot: "only-on-failure",
video: "on",
trace: "on",
//Included for API Testing
baseURL: 'https://restful-booker.herokuapp.com/',
extraHTTPHeaders: {
},//End of API Testing
},
testDir: './tests',
testMatch: ["apiTestSamples.test.ts"],
reporter: [["list"],
["json", { outputFile: "jsonReports/MyJsonReport.json" }],
["html", { open: "always" }]
]
}
export default config;

Create a file apiTestSample.test.ts under /test.

import test, { expect } from "@playwright/test";
import { bookingDetails } from "../test-data/booking_details.json"

test.describe('API Testing - CRUD', () => {

}

Get Method

i. Get all the records :

test('To get all the booking details', async ({ request }) => {
const response = await request.get("/booking");
console.log(await response.json());
expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);
})

Request.get() — Sends HTTP(S) GET request and returns its response. The method will populate request cookies from the context and update context cookies from the response. The method will automatically follow redirects.

ii. Get the booking details by using query param and assert its response :

Request parameters can be configured with params option and will be serialized into the URL search parameters:

test('To get specific booking details', async ({ request }) => {
const response = await request.get('/booking/1', {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
})
console.log(await response.json());
expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);
const respBody = await response.json();
expect(respBody.firstname).toBe('Jim')
})
test('To get subset of booking details using query parameters', async ({ request }) => {
const response = await request.get('/booking', {
params: {
firstname: "Susan",
lastname: "Jackson"
},
});
console.log(await response.json());
expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);
})

Here, the get() will return response fixture which will have status, response body etc. This can be validated with various assert methods.

Post Method

Sends HTTP(s) POST request and returns its response. In this section, we will see how a POST call can be made by passing the static data in the body.

Implementation

To create a new booking, we will send a POST request to the /booking endpoint, and need to provide the request body or payload in the request. Print the JSON response in the console to ensure that have received the entire response. As the endpoint will return a status code of 200 and ok as a status message, we should confirm this using an expect condition.

Also, we can verify if the response body has certain data:

firstname

lastname

totalprice

depositpaid

i. Create a booking by passing data :

test('should be able to create a booking', async ({ request }) => {
const response = await request.post("/booking", {
data: {
"firstname": "John",
"lastname": "Britt Bat",
"totalprice": 121,
"depositpaid": true,
"bookingdates": {
"checkin": "2023-06-01",
"checkout": "2023-06-15"
},
"additionalneeds": "Breakfast"
}
});
console.log("Response in JSON : ....." + await response.json());
expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);
const responseBody = await response.json()
console.log("responseBody......" + responseBody.booking.firstname)
expect(responseBody.booking).toHaveProperty("firstname", "John");
expect(responseBody.booking).toHaveProperty("lastname", "Britt Bat");
expect(responseBody.booking).toHaveProperty("totalprice", 121);
expect(responseBody.booking).toHaveProperty("depositpaid", true);
});

ii. Create a booking by passing Json file

To pass json data as payload, create a .json file (booking_details.json) under project/test-data/.

Content of .json file is,

{
"bookingDetails": {
"firstname": "Peter",
"lastname": "Thomas ",
"totalprice": 131,
"depositpaid": true,
"bookingdates": {
"checkin": "2023-06-01",
"checkout": "2023-06-15"
},
"additionalneeds": "Breakfast"
}
}

Now, use this .json as payload,

import test, { expect } from "@playwright/test";
import { bookingDetails } from "../test-data/booking_details.json"

test.describe('API Testing - CRUD', () => {
test.only('should be able to create a booking with JSON', async ({ request}) => {
console.log(bookingDetails)
const response = await request.post("/booking", {
data: bookingDetails,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
}
});
const responseBody = await response.json();
expect(responseBody.booking).toHaveProperty("lastname", "Thomas");
expect(responseBody.booking).toHaveProperty("totalprice", 131);
expect(responseBody.booking).toHaveProperty("depositpaid", true);
})
// @ts-check

To verify the post() is successful, we validate some of its property values to have expected data.

Put Method

Put() is to update the data. In this API we need authorization token to update. So we use,

'Authorization': 'Basic YWRtaW46cGFzc3dvcmQxMjM=',
test('To update the booking details', async ({ request }) => {
// PUT
const updateRequest = await request.put('/booking/172', {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Basic YWRtaW46cGFzc3dvcmQxMjM=',
},
data: {
"firstname": "James",
"lastname": "Brown",
"totalprice": 111,
"depositpaid": true,
"bookingdates": {
"checkin": "2023-06-01",
"checkout": "2023-06-15"
},
"additionalneeds": "Breakfast"
}
});
console.log("Response Body...." + await updateRequest.body());
await expect(updateRequest.ok()).toBeTruthy();
await expect(updateRequest.status()).toBe(200);
const updatedResponseBody = await updateRequest.json()
expect(updatedResponseBody).toHaveProperty("firstname", "James");
expect(updatedResponseBody).toHaveProperty("lastname", "Brown");
expect(updatedResponseBody).toHaveProperty("totalprice", 111);
expect(updatedResponseBody).toHaveProperty("depositpaid", true);
})

Delete Method

Let we delete the booking and assert its response code.

 test('To delete the booking details', async ({ request }) => {
const deleteRequest = await request.delete('/booking/1', {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic YWRtaW46cGFzc3dvcmQxMjM=',
}
});
expect(deleteRequest.status()).toEqual(201);
expect(deleteRequest.statusText()).toBe('Created');
})

Reference

API testing | Playwright

APIRequestContext | Playwright

GitHub Repo

A2ZTestAutomation/PW_APITestSample (github.com)

Conclusion

Thus, in Playwright web API Testing can be done effectively. This could be included as part of Continuous Testing in CI/CD pipeline.

--

--

Anandhi K

DevOps Test Automation Consultant, Trainer and Blogger in Cypress, Selenium, Cucumber, Playwright & CI/CD Tools.