</>

Technology

Playwright

Difficulty

Advanced

Interview Question

How would you mock a failed API response to test error handling in your application?

Answer

Scenario: Testing Error Handling via Mocked API Failures

One of the most valuable uses of Playwright's network interception is testing how your app handles API errors — without needing a broken backend.

Mock a 500 Internal Server Error

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

test('shows error toast when product API fails', async ({ page }) => {
  // Return 500 error for the products endpoint
  await page.route('/api/products', route =>
    route.fulfill({
      status: 500,
      contentType: 'application/json',
      body: JSON.stringify({
        error: 'Internal Server Error',
        message: 'Database connection failed',
      }),
    })
  );

  await page.goto('/products');

  // Verify error state in UI
  await expect(page.locator('.error-toast')).toBeVisible();
  await expect(page.locator('.error-toast')).toContainText('Something went wrong');
  await expect(page.locator('.product-list')).not.toBeVisible();
  await expect(page.getByRole('button', { name: 'Retry' })).toBeVisible();
});

Mock a 404 Not Found

TypeScript
test('shows not found page for invalid product', async ({ page }) => {
  await page.route('/api/products/99999', route =>
    route.fulfill({
      status: 404,
      json: { error: 'Product not found' },
    })
  );

  await page.goto('/products/99999');
  await expect(page.getByRole('heading')).toContainText('Product Not Found');
  await expect(page.getByRole('link', { name: 'Back to Products' })).toBeVisible();
});

Mock a 401 Unauthorized

TypeScript
test('redirects to login on 401 response', async ({ page }) => {
  // Simulate expired session
  await page.route('/api/user/profile', route =>
    route.fulfill({
      status: 401,
      json: { error: 'Session expired. Please log in again.' },
    })
  );

  await page.goto('/profile');

  // App should redirect to login
  await expect(page).toHaveURL('/login');
  await expect(page.locator('.session-message')).toContainText('Session expired');
});

Mock Network Failure (No Connection)

TypeScript
test('shows offline message when network fails', async ({ page }) => {
  // Simulate complete network failure
  await page.route('/api/**', route => route.abort('failed'));

  await page.goto('/dashboard');
  await expect(page.locator('.offline-banner')).toBeVisible();
  await expect(page.locator('.offline-banner')).toContainText('Check your connection');
});

Mock Slow Response (Loading State)

TypeScript
test('shows loading skeleton during slow API', async ({ page }) => {
  let resolveDelay: () => void;

  await page.route('/api/products', route =>
    new Promise(resolve => {
      resolveDelay = resolve;
      // Keep the route "pending" for 3 seconds
      setTimeout(() => {
        resolve(undefined);
        route.fulfill({ json: { products: [] } });
      }, 3000);
    })
  );

  await page.goto('/products');

  // Verify loading skeleton shows during the delay
  await expect(page.locator('.skeleton-loader')).toBeVisible();
  await expect(page.locator('.product-list')).not.toBeVisible();

  // After 3s, data loads
  await expect(page.locator('.product-list')).toBeVisible({ timeout: 5000 });
  await expect(page.locator('.skeleton-loader')).not.toBeVisible();
});

Test Retry Mechanism

TypeScript
test('retry button fetches data again after error', async ({ page }) => {
  let callCount = 0;

  await page.route('/api/products', async route => {
    callCount++;
    if (callCount === 1) {
      // First call fails
      await route.fulfill({ status: 500, json: { error: 'Error' } });
    } else {
      // Second call succeeds (after user clicks retry)
      await route.fulfill({ status: 200, json: [{ id: 1, name: 'Laptop' }] });
    }
  });

  await page.goto('/products');
  await expect(page.locator('.error-message')).toBeVisible();

  // Click retry
  await page.getByRole('button', { name: 'Retry' }).click();

  // Data loads successfully on second attempt
  await expect(page.locator('.product-card')).toBeVisible();
  expect(callCount).toBe(2);
});

Follow AutomateQA

Related Topics