| |

API Testing With Playwright: The Complete Guide Beyond UI Automation

Playwright is widely known for UI automation, but it also provides robust capabilities for API testing — allowing teams to validate both frontend and backend within a single framework. No more maintaining separate tools for API and UI tests.

Contents

Why API Testing in Playwright?

  • Reduces dependency on UI for test data setup
  • Improves test execution speed (API calls are 10-50x faster than UI interactions)
  • Enables early validation of backend logic before UI is ready
  • Supports true end-to-end testing: create data via API, validate via UI

Basic API Requests

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

test('GET request - fetch users', async ({ request }) => {
  const response = await request.get('/api/users');
  
  expect(response.ok()).toBeTruthy();
  expect(response.status()).toBe(200);
  
  const body = await response.json();
  expect(body.users).toHaveLength(10);
  expect(body.users[0]).toHaveProperty('email');
});

test('POST request - create user', async ({ request }) => {
  const response = await request.post('/api/users', {
    data: {
      name: 'Test User',
      email: 'test@example.com',
      role: 'admin'
    }
  });
  
  expect(response.status()).toBe(201);
  const user = await response.json();
  expect(user.id).toBeDefined();
});

Authentication Flows

test('API with Bearer token auth', async ({ request }) => {
  // Login to get token
  const loginResponse = await request.post('/api/auth/login', {
    data: { email: 'admin@test.com', password: 'secure123' }
  });
  const { token } = await loginResponse.json();
  
  // Use token for authenticated request
  const response = await request.get('/api/admin/dashboard', {
    headers: { 'Authorization': 'Bearer ' + token }
  });
  
  expect(response.ok()).toBeTruthy();
});

Hybrid Pattern: API Setup + UI Validation

test('create product via API, verify in UI', async ({ page, request }) => {
  // API: Create test data (fast, reliable)
  const response = await request.post('/api/products', {
    data: { name: 'Test Widget', price: 29.99, category: 'electronics' }
  });
  const product = await response.json();
  
  // UI: Verify it renders correctly
  await page.goto('/products/' + product.id);
  await expect(page.getByText('Test Widget')).toBeVisible();
  await expect(page.getByText('$29.99')).toBeVisible();
});

Response Schema Validation

test('validate response schema', async ({ request }) => {
  const response = await request.get('/api/products/1');
  const product = await response.json();
  
  // Validate structure
  expect(product).toHaveProperty('id');
  expect(product).toHaveProperty('name');
  expect(product).toHaveProperty('price');
  expect(typeof product.price).toBe('number');
  expect(product.price).toBeGreaterThan(0);
  
  // Validate response time
  const timing = response.headers()['x-response-time'];
  expect(parseInt(timing)).toBeLessThan(500);
});

Mocking External APIs

test('mock payment gateway', async ({ page }) => {
  // Intercept Stripe API calls
  await page.route('**/api.stripe.com/**', route => {
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ id: 'ch_mock', status: 'succeeded' })
    });
  });
  
  await page.goto('/checkout');
  await page.getByRole('button', { name: 'Pay Now' }).click();
  await expect(page.getByText('Payment successful')).toBeVisible();
});

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.