Problem definition
Playwright tests can sometimes depend on external services such as APIs, which might happen to be unavailable at times. In this case there are several options for executing these tests adequately, as described below.
- Actually call the request, such as a development environment.
- If the backend cannot be reached, the test will fail.
- Start your own service such as Wiremock and define the responses.
- Simulate the corresponding HTTP call.
This blog article will illustrate the latter and describe how to simulate the corresponding HTTP calls.
Example
If you call up a specific website in your test, further implicit requests are often made. This means that the website under test will trigger further HTTP calls on the client side during the page load. The following examples illustrate this procedure step by step.
For upcoming examples, a service of
jsonplaceholder
is being used.
General test
In the test implemented below, we imagine that a very specific request is carried out implicitly. To make this clear, we lead our test directly to the endpoint in order to develop a basis for further extensions. In this case, it is /todos/1
.
This pattern is not practical and only serves the purpose of simplicity.
1import { test } from "@playwright/test"; 2 3test.describe('Description', async () => { 4 5test('mytest', async ({ page }) => { 6 // given 7 // no prerequisites 8 9 // when 10 await page.goto('https://jsonplaceholder.typicode.com/todos/1') 11 12 // then 13 // expectations... 14}) 15 16}
At the time of this blog post, GET /todos/1
returns the following response.
1{ 2 "userId": 1, 3 "id": 1, 4 "title": "delectus aut autem", 5 "completed": false 6}
Generic extension
Once we implemented the test from the previous example, we can start intercepting the request and executing a callback. The callback itself is a function that is executed as soon as Playwright recognizes this route
. The following test, which is a extended test from above, continues the request after a console.log
and the response remains the same.
1import { Route, test } from "@playwright/test"; 2 3test.describe('Description', async () => { 4 test('mytest', async ({ page }) => { 5 6 // given 7 // for this route ... 8 await page.route('https://jsonplaceholder.typicode.com/todos/1', async (route: Route) => { 9 10 // ... execute this callback 11 console.log('invoked HTTP call to given route') 12 await route.continue() 13 }) 14 15 // when 16 await page.goto('https://jsonplaceholder.typicode.com/todos/1') 17 18 // then 19 // expectations ... 20 }) 21})
As mentioned above, this form of picking out a request to an endpoint is not common and is used for illustrative purposes. This test does not currently test a website, but only a request where the response is also mocked.
Practical example
The test is now going to be converted. We access the website in our test. This call triggers further requests. One of them is a request that goes to the style.css
stylesheet. This should not actually be executed. In this example, the response is replaced and returned as text/plain
. This means that no styling is loaded on the page.
For this purpose, the Route interface offers the fulfill()
function to define this response.
1import { Route, test } from "@playwright/test"; 2 3test.describe('Description', async () => { 4 test('mytest', async ({ page }) => { 5 6 // given 7 // for this route ... 8 await page.route( 9 'https://jsonplaceholder.typicode.com/style.css', 10 11 // ... execute this callback 12 async (route: Route) => { 13 console.log('invoked HTTP call to given route') 14 15 // define this response 16 await route.fulfill({ 17 status: 200, 18 contentType: 'text/plain', 19 body: 'No stylesheet for you', 20 }) 21 }, 22 ) 23 24 // when 25 await page.goto('https://jsonplaceholder.typicode.com/') 26 27 // then 28 // expectations ... 29 }) 30})
It is recommended to refactor the function so that it can be used in the
1test.beforeEach(() => {})
block. Therefore it's possible to invoke the function before every test.
Out of scope but an FYI: The
route.request()
function returns the entire request, making it possible to filter certain requests and act accordingly. In order not to complicate the examples, these will not be discussed in detail.
Playwright UI
The Playwright UI, actually a Chromium
browser, also offers the well-known Network
tab. Here you can see the mocked request and also analyze the body in more detail.
Actual request | Mocked request |
---|---|
Actual styling (actual request) | No styling (mocked request) |
---|---|
Limitations
Only network requests that are triggered from the browser can be mocked. These can be found in the network tab of the browser's developer tools. The reason for this is that Playwright is a library that simulates the user and thus the interaction with the browser.
If the web application to be tested is implemented with a full-stack framework such as NextJs
or Remix
, it is also possible for HTTP calls to be called in the backend.
The browser has no access to these and cannot be mocked.
Conclusion
As described in this blog post, there are not many steps involved in mocking a request. Especially if you do not have any possibility to access the backend, the problem of specific expected responses is being solved.
More articles
fromEge Inanc
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
More articles in this subject area
Discover exciting further topics and let the codecentric world inspire you.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Ege Inanc
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.