Skip to content

Web Test Automation: Intercepting/Mocking network calls with Playwright #111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,8 @@
}
}
}
},
"cli": {
"analytics": "89ea0e7e-4b4f-49c2-902c-21bc47d5ec71"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Web Test Automation: Intercepting/Mocking Network Calls with Playwright

Nowadays, QAs are much more into test automation to improve the quality and efficiency of the testing. Due to the technical limitations, the test cases are categorized as `Not Possible To Automate`.

![](assets/Interceptor.png)

Authors: Dilshan Fernando
Date: unpublished
Category: qa

tags: playwright

---
This blog provides a solid solution where tester use a proxy tool to intercept an API request/response to perform validations on a web app. Playwright provides APIs to monitor and intercept browser network traffic, both HTTP and HTTPS. Any requests a page does, including XHRs and fetch requests, can be tracked, modified, and handled.
As the next step, look at the benefits and possibilities of intercepting and mocking HTTP requests/responses in your Playwright test.

## What are the benefits of this?

1. During manual web testing, testers use proxy tools such as [Proxyman](https://proxyman.io/) and [Charles](https://www.charlesproxy.com/) to capture the traffic between your app and the SSL Web Server. Breakpoint Tool helps you to intercept Requests/Responses data on the fly without changing any client code. If the tester can automate these manual steps, then everyone knows how it would be beneficial for the final quality of the app.

2. No need to maintain mock servers or depend on client test data 100%. While using the client data tester can,
- Control the quality of test data
- Increase data volume/varieties
- Readiness and availability of data
3. At last, the automation coverage can be increased by automating manual test cases which depends on the proxy tools.

---

## Next look at the implementation

Playwright allows you to intercept network requests by using the route method. You can use this method to change or log the network traffic, or even block certain requests.

Playwright provide 5 methods,
- abort -> Aborts the route's request.
- continue -> Continue route's request with optional overrides.
- fallback -> When several routes match the given pattern, they run in the order opposite to their registration. That way the last registered route can always override all the previous ones.
- fetch -> Performs the request and fetches the result without fulfilling it so that the response can be modified and then fulfilled.
- fulfill -> Fulfills route's request with a given response.

### How to integrate this approach into your test framework?

Next look at the `netwrokInterceptor.ts`

```typescript
import { Page } from "@playwright/test";
import * as fs from 'fs';
import * as path from 'path';

export class NetworkInterceptor {
constructor(private page: Page) { }
/**
*
* @param urlToIntercept Response URL which need to Mock
* @param options [filePath] relative path for the Json response body
* @param options [statusCode] Mock response status code
*/
async interceptResponse(urlToIntercept: string, options?: { filePath?: string, statusCode?: number }) {
var newResponseBody = readJsonFile(options.filePath);

await this.page.route(urlToIntercept, async (route) => {
console.log('Intercepted response:', route.request().url());
// Make the original request
const response = await route.fetch();
// if mock response code is unavialble, then use the original response code
const mockResponsecode = (typeof options.statusCode !== 'undefined') ? options.statusCode : Number(response.status)
// Replace the response with the modified data
route.fulfill({
status: mockResponsecode,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newResponseBody),
});
});
}
/**
*
* @param urlToIntercept
* @param options [filePath] relative path for the Json request body
*/
async interceptRequest(urlToIntercept: string, options?: { filePath?: string}) {
var newRequestBody = readJsonFile(options.filePath);

await this.page.route(urlToIntercept, async (route) => {
console.log('Intercepted request:', route.request().url());
// Replace the request with the modified data
route.continue({
postData: newRequestBody
});
});
}
}

/**
*
* @param filePath relative location of the json file
* @returns JSON Object
*/
function readJsonFile(filePath: string) {
const rawData = fs.readFileSync(path.resolve(__dirname, filePath), 'utf-8');
return JSON.parse(rawData);
}
```
Let's go through the implementation at a high level.

The method `InterceptResponse` is a generic method that can be used or modified (based on the use case) to intercept any response you want.
The method `InterceptRequest` is a generic method that can be used or modified (based on the use case) to intercept any request you want.

> 💡
> You only need to add a new parameter to the option based on what you need. This way you can modify the same method without breaking the existing code.

### How to use this new method in Test class?

The tricky part is, how to invoke this method in your test. The most common mistakes are,

```typescript
await identityPage.loginButton.click()
await page.waitForResponse(urlToIntercept)
```
OR
```typescript
await page.waitForResponse(urlToIntercept)
await identityPage.loginButton.click()
```
In the first approach, as soon as the login button is clicked request and response occurred, but the test execution is waiting for a response.(line# 2)

In the second approach, the request has not happened because the click event hasn't happened yet.

To resolve this you must use `await Promise.all([]);` to wait for both interactions to resolve.

```typescript
const filePath = '../data/json-files/internal-server-error.json'
const urlToIntercept = env.BASE_URL + '/api/transaction-manager/client-api/v2/transactions?size=5&orderBy=bookingDate&direction=DESC';
networkInterceptor.interceptResponse(urlToIntercept, { filePath, statusCode: 500 })

await Promise.all([
page.waitForResponse(urlToIntercept),
identityPage.loginButton.click(),
]);
```


> ✏️
> You need to add the server error JSON response to the project and replace the value for `filepath`

## Conclusion

The [page.route](https://playwright.dev/docs/api/class-page#page-route) method of Playwright allows you to intercept HTTP requests and return a mocked response. Because you are fully in control of the response, this enables you to create edge cases to cover all the possible scenarios quickly without introducing a lot of overhead.
The Playwright API is flexible enough to be used in different ways. You can just create a mocked response, return an error, or you can make the original request and alter the response.

## References
Official documentation for [Network](https://playwright.dev/docs/network) interception.


3 changes: 2 additions & 1 deletion routes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
/2023/12/13/angular-micro-frontends
/principles/innersource
/principles/software-engineering
/unpublished/web-test-automation-intercepting-mocking-network-calls-with-playwright
/category/tech-life
/category/devops
/category/backend
/category/career
/category/frontend
/category/sdlc
/404
/404