Can we use Playwright for API Automation Project? Yes!

Numanhan Duran
4 min readNov 6, 2023

Greetings to everyone. After a long time, I will talk to you about a framework I have been using recently: Playwright. Almost everyone who works in the testing field has heard the name Playwright at one time or another. But in most test automation projects, Playwright is used for UI tests. After doing some research as a team, we decided why not use it for an API test automation project and started researching.At this point, we have a nice project where we ran our API tests very successfully.Let me tell you how to set up an API test automation project. We can finish the topic with a few examples.

Setup

Before installing Playwright, make sure node.js is installed in your environment.

# Install playwright
npm install @playwright/test

# Create an empty project directory
mkdir api-test-project
cd api-test-project
npm init -y

Let’s take a booking endpoint in this structure as an example. I assume we will test GET, POST and DELETE requests in this structure. There is one point we need to pay attention to when creating the project structure. We need to avoid code repetition when building the structure. We should take care to write code that is as clean and readable as possible. That’s why I chose to file my services and tests in isolation.

The project structure will look something like this:

booking-api-test/
├── tests/
│ ├── get-booking.spec.ts
│ ├── post-booking.spec.ts
│ ├── delete-booking.spec.ts
│ └── ...
├── services/
│ ├── api-service.ts
│ ├── auth-service.ts
│ └── ...
├── package.json
├── tsconfig.json
└── .env

Now let’s code our services and tests. I will create a structure assuming there is authentication in this structure. For this authentication, I assume that there is an accessToken value in the Headers.

Step 2: Creating Service Files

api-service.ts

import { Page } from '@playwright/test';

export class APIService {
private page: Page;

constructor(page: Page) {
this.page = page;
}

async getBooking(bookingId: string) {
return await this.page.route(`**/bookings/${bookingId}`).fetch();
}

async createBooking(accessToken: string, bookingData: any) {
return await this.page.route('**/bookings').fetch({
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(bookingData),
});
}

async deleteBooking(accessToken: string, bookingId: string) {
return await this.page.route(`**/bookings/${bookingId}`).fetch({
method: 'DELETE',
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});
}
}

auth-service.ts

import { Page } from '@playwright/test';

export class AuthService {
private page: Page;

constructor(page: Page) {
this.page = page;
}

async getAccessToken(username: string, password: string) {
// API call required to obtain access tokens
// For example, you can authenticate with OAuth 2.0
}
}

Step 3: Creating API Test Files

Now we can create the test files. As an example, let’s create files such as get-booking.spec.ts, post-booking.spec.ts and delete-booking.spec.ts.

get-booking.spec.ts

import { test, expect } from '@playwright/test';
import { APIService, AuthService } from '../services';

test('GET Booking Test', async ({ page }) => {
const authService = new AuthService(page);
const accessToken = await authService.getAccessToken('username', 'password');

const apiService = new APIService(page);
const response = await apiService.getBooking('bookingId', accessToken);

// Test asertions
expect(response.status()).toBe(200);
const bookingData = await response.json();
expect(bookingData).toHaveProperty('id', 'bookingId');
});

post-booking.spec.ts

import { test, expect } from '@playwright/test';
import { APIService, AuthService } from '../services';

test('POST Booking Test', async ({ page }) => {
const authService = new AuthService(page);
const accessToken = await authService.getAccessToken('username', 'password');

const bookingData = {
// Booking data
// According to your endpoint you can pass the data which needed
};

const apiService = new APIService(page);
const response = await apiService.createBooking(accessToken, bookingData);

// Test asertions
expect(response.status()).toBe(201);
const createdBooking = await response.json();
expect(createdBooking).toHaveProperty('id');
});

delete-booking.spec.ts

import { test, expect } from '@playwright/test';
import { APIService, AuthService } from '../services';

test('DELETE Booking Test', async ({ page }) => {
const authService = new AuthService(page);
const accessToken = await authService.getAccessToken('username', 'password');

const apiService = new APIService(page);
const response = await apiService.deleteBooking('bookingId', accessToken);

// Test asertions
expect(response.status()).toBe(204);
});

This structure actually shows you how to obtain an accessToken in a very simple way and then how you can use it. I tried to explain it in a simple way. Other paths can also be followed.

Creating the project structure may vary depending on you and the project you are working on. For example, you can handle this globally without having to call a method to access the accessToken in each test.

Note: Playwright, like other frameworks, includes beforeAll(), beforeEach() and similar methods. It would be beneficial for you to read and follow the document.

Step 4: CI/CD Integration

Below I have prepared a very simple sample yaml file for Github Actions. This, like other approaches, can be shaped according to the needs of your project. I’ll leave you with a simple example just to give you an idea, the rest is up to you :)

name: API Test CI/CD

on:
push:
branches:
- main

jobs:
api_test:
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v2

- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '14'

- name: Install Dependencies
run: npm install

- name: Run API Tests
run: npm test

This GitHub Actions example will run API tests on every push. You can increase your security by storing confidential information in the .env file.

I hope it was useful. In this article, I just wanted to talk about how to use it and how you can use it as a beginner. Of course, you will have your own approaches and structures that you will establish in other ways. I recommend you read the Playwright document. At the same time, TypeScript also provides us with flexibility. You can also use different libraries. I may expand my article by giving some more details in my next article.

--

--