API Mocking in Playwright

Anandhi K
6 min readMar 15, 2024

API Mocking is to imitate and test a real API by emulating its responses, response behaviors, and endpoints.

In this article we will discuss on,

1. What is API mocking?

2. Advantages of API Mocking

3. API mocking in Playwright

What is API Mocking?

API mocking is simulating the behavior of an API without relying on real data. API mocking is a way of creating realistic examples to simulate the expected response of an API. This involves creating “mock” versions of the various components that make up an API, such as data models and endpoints. Further interaction can be done as if they were real APIs.

Advantage of API Mocking :

Some of the key benefits of API Mocking are,

Cost Effective :

The process allows developers to continue working on the project even if the actual API is not yet available or stable. This eliminates the need to wait for the API’s availability or pay for third-party services during development.

Quick Feedback :

By providing developers with a realistic environment to test their code and test data, API mocking ensures that feedback is more accurate. You can more quickly find bugs

More Secured :

With API mocking, developers are working with fake data, reducing the risk of accessing sensitive information or leaking private data.

Improved Reliability :

API mocking allows developers to create specific testing scenarios to simulate different conditions. Additionally, API mocking enables developers to accurately replicate the behavior of the real API.

API Mocking in Playwright :

There are 3 ways we can Mock API in Playwright :

a. Mock API request

b. Modify API response

c. Mocking with HAR files

Web APIs are usually implemented as HTTP endpoints. Playwright provides APIs to mock and modify network traffic, both HTTP and HTTPS. Any requests that a page does, including XHRs and fetch requests, can be tracked, modified and mocked. With Playwright you can also mock using HAR files that contain multiple network requests made by the page.

Mock API Request :

In the above page it is displaying list of popular tags from API. Now we are going to mock this API request.

test("Mock list of tags and doesn't call api", async ({ page }) => {

// Mock the api call before navigating
// https://conduit-realworld-example-app.fly.dev/api/tags
await page.route('*/**api/tags', async route => {
const json = {
'tags': ["playwright", "Mock API"]
};
await route.fulfill({ json });
});
// Go to the page
await page.goto('https://conduit-realworld-example-app.fly.dev/');
// Assert that the given tag is visible
await expect(page.getByText('playwright')).toBeVisible({ timeout: 30000})
})

This will display,

Without calling API, the request was fulfilled with the mock data.

Modify API Response :

Sometimes, it is essential to make an API request, but the response needs to be patched to allow for reproducible testing. In that case, instead of mocking the request, one can perform the request and fulfill it with the modified response.

In the example below we intercept the call to the tags API and add a new tag called ‘playwright’, to the data. We then go to the url and assert that this data is there:

test('gets the json from api and adds a new tag', async ({ page }) => {
// Get the response and add to it
await page.route('https://conduit-realworld-example-app.fly.dev/api/tags', async (route) => {
const response = await route.fetch()
const json = await response.json()
console.log(json)
//tags is a name of the json array in json object.
//We want to push element into this array
// To add at the end of an array
// json.tags.push('playwright')
//To add at the beginning of an array
json.tags.unshift('playwright')
// Fulfill using the original response, while patching the response body
// with the given JSON object.
await route.fulfill({ response, json });
});

In the above code, we fetch response from route object, add value and fulfils route with response and json objects.

Here tags are listed in an json array named ‘tags’ like,

{"tags":["implementation","backend"

So I have used,

    json.tags.unshift('playwright')

In case if no array name is given, directly we can add as below

json.unshift('playwright')

There are two ways we can add element to an json array,

a. Unshift()

b. Push()

json.push() is to add element at the end of an array and json.unshift() is to add element at the beginning of an array.

In this page, there are 50+ tags and it is displaying first half of them so we could not validate the newly added tag. Hence I use unshift() to add at the beginning.

When we run the above code will display the page as below,

In the trace of our test we can see that the API was called and the response was modified. By inspecting the response we can see that our new tag was added to the list.

Mocking with HAR file :

What is HAR File?

HAR file is a JSON archive file format that stores browsing data over multiple browsers. It works by storing a data session between the client and server. In other words, a HAR file is used to save all the web requests made on the browser. More than often the file is used to analyze, and find vulnerabilities.

HAR is expanded as HTTP Archive Format and can be classified as a JSON archive file format. Developers and security researchers use this file to detect bugs and vulnerabilities. Furthermore, HAR file makes it easier to solve website performance issues across multiple browsers. It includes HTTP response and request headers. HAR file comes with a “.har” extension.

To mock with HAR file, we need to

a. Record a HAR file

b. Commit the HAR file alongside the tests

c. Update HAR file

d. Route requests using the saved HAR files in the tests

a. Record a HAR File :

To record a HAR file we use page.routeFromHAR() or browserContext.routeFromHAR()

method. This method takes in the path to the HAR file and an optional object of options. The options object can contain the URL so that only requests with the URL matching the specified glob pattern will be served from the HAR File. If not specified, all requests will be served from the HAR file.

Setting update option to true will create or update the HAR file with the actual network information instead of serving the requests from the HAR file. Use it when creating a test to populate the HAR with real data.

test('Record the HAR file', async ({ page }) => {
// Get the response from the HAR file
await page.routeFromHAR('./hars/tags.har', {
url: '*/**/api/tags',
update: true,
});

// Go to the page
await page.goto('https://conduit-realworld-example-app.fly.dev/');
});

b. Commit the HAR file alongside the tests :

This will generate tags.har and xxxxx.json file under /hars in our project folder as below,

c. Update a HAR file :

This .json can be updated with new tags. I have added “playwright”.

d. Route requests using the saved HAR files in the tests

Now that you have the HAR file recorded and modified the mock data, it can be used to serve matching responses in the test. For this, just turn off or simply remove the update option. This will run the test against the HAR file instead of hitting the API.

test('Read the HAR file', async ({ page }) => {
// Get the response from the HAR file
await page.routeFromHAR('./hars/tags.har', {
url: '*/**/api/tags',
update: false,
});

// Go to the page
await page.goto('https://conduit-realworld-example-app.fly.dev/');

// Assert that newly added tag is visible
await expect(page.getByText('playwright')).toBeVisible();
});

In the trace of our test we can see that the route was fulfilled from the HAR file and the API was not called.

Reference :

Mock APIs | Playwright

--

--

Anandhi K

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