DEV Community

Cover image for End-to-End API Testing with Playwright
Aswani Kumar
Aswani Kumar

Posted on

End-to-End API Testing with Playwright

Table of Contents

Introduction

End-to-end testing is a crucial part of software development to ensure that your application works seamlessly from start to finish. While Playwright is widely known for UI automation, it also provides robust capabilities for API testing, allowing you to validate your backend services alongside your frontend workflows. In this blog, we’ll explore how to perform effective end-to-end API testing with Playwright.

1. Why Use Playwright for API Testing?

Playwright’s API testing capabilities integrate directly with its browser automation, enabling you to:

  • Test APIs in isolation or in conjunction with UI flows.
  • Mock or intercept network requests to simulate edge cases.
  • Validate frontend-backend communication in real-time.

This combination simplifies complex workflows and ensures comprehensive test coverage.

2. Setting Up API Testing in Playwright


a. Install Playwright

Ensure you have Playwright installed in your project:

npm install @playwright/test
Enter fullscreen mode Exit fullscreen mode


b. Create a Playwright Test File

Create a test file, e.g., api.test.ts, to write API tests:

touch api.test.ts
Enter fullscreen mode Exit fullscreen mode

3. Writing API Tests with Playwright

Playwright’s page.request object provides methods for making HTTP requests.


a. Basic API Test

Here’s an example of testing a GET endpoint:

import { test, expect } from '@playwright/test';

test('GET request example', async ({ request }) => {
  const response = await request.get('https://jsonplaceholder.typicode.com/posts/1');
  expect(response.ok()).toBeTruthy();
  const data = await response.json();
  expect(data.id).toBe(1);
});
Enter fullscreen mode Exit fullscreen mode


b. Testing POST Requests

Validate a POST request by sending data and verifying the response:

test('POST request example', async ({ request }) => {
  const payload = { title: 'foo', body: 'bar', userId: 1 };
  const response = await request.post('https://jsonplaceholder.typicode.com/posts', {
    data: payload,
  });
  expect(response.status()).toBe(201);
  const data = await response.json();
  expect(data.title).toBe('foo');
});
Enter fullscreen mode Exit fullscreen mode

4. Mocking and Intercepting API Calls


a. Intercepting Requests

Use page.route to intercept and modify network requests:

test('Intercept API call', async ({ page }) => {
  await page.route('https://api.example.com/data', route => {
    route.fulfill({
      status: 200,
      body: JSON.stringify({ success: true, message: 'Mocked Response' }),
    });
  });

  await page.goto('https://example.com');
  const text = await page.locator('#response-message').innerText();
  expect(text).toBe('Mocked Response');
});
Enter fullscreen mode Exit fullscreen mode


b. Simulating API Failures

Test how your application handles API errors:

test('Simulate API failure', async ({ page }) => {
  await page.route('https://api.example.com/data', route => {
    route.fulfill({
      status: 500,
      body: JSON.stringify({ error: 'Internal Server Error' }),
    });
  });

  await page.goto('https://example.com');
  const error = await page.locator('#error-message').innerText();
  expect(error).toBe('Internal Server Error');
});
Enter fullscreen mode Exit fullscreen mode

5. Combining UI and API Testing

Playwright excels at combining UI and API tests for end-to-end scenarios.

Example: User Registration Flow

  1. Use API calls to seed test data.
  2. Automate the UI to verify the frontend reflects API responses.
test('User registration end-to-end', async ({ request, page }) => {
  // Seed user data via API
  const response = await request.post('https://api.example.com/register', {
    data: { username: 'testuser', password: 'password123' },
  });
  expect(response.ok()).toBeTruthy();

  // Verify user login via UI
  await page.goto('https://example.com/login');
  await page.fill('#username', 'testuser');
  await page.fill('#password', 'password123');
  await page.click('#login-button');
  await expect(page).toHaveURL('https://example.com/dashboard');
});
Enter fullscreen mode Exit fullscreen mode

6. Debugging API Tests


a. Logging Requests and Responses

Log details to debug API interactions:

const response = await request.get('https://api.example.com/data');
console.log(await response.json());
Enter fullscreen mode Exit fullscreen mode


b. Using Playwright Tracing

Capture traces to analyze test execution:

await page.tracing.start({ screenshots: true, snapshots: true });
await page.goto('https://example.com');
await page.tracing.stop({ path: 'trace.zip' });
Enter fullscreen mode Exit fullscreen mode

7. Best Practices for API Testing with Playwright

  1. Use Fixtures: Define reusable setups for API endpoints and test data.
  2. Test Edge Cases: Simulate error conditions and boundary values.
  3. Integrate with CI/CD: Automate API tests in your pipeline for continuous validation.
  4. Document APIs: Use tools like Swagger to understand API specs before writing tests.
  5. Isolate Tests: Avoid dependencies between tests to ensure reliability.

Conclusion

End-to-end API testing with Playwright enhances your ability to validate both backend services and frontend workflows comprehensively. By leveraging Playwright’s API testing capabilities, you can simulate real-world scenarios, intercept network requests, and ensure your application delivers a seamless user experience.

What are your favourite features of Playwright for API testing? Share your thoughts in the comments below!

Top comments (0)