DEV Community

Styrow.dev
Styrow.dev

Posted on Originally published at styrow.dev

Designing Resilient UI Tests: Simulating Dynamic Backend Failures in Playwright

💥 Playwright Hardcore: Dynamic Backend Failures in UI Tests!
Your SPA uses microservices. Test UI resilience against ProductService 500s or timeouts, ensuring proper error handling without over-mocking.


📌 Problem Statement
• SPAs need robust UI error handling for flaky microservices.
• Simulate dynamic backend failures (ProductService 500s/timeouts) in Playwright to validate UI degradation/retries.
• Goal: Realistic, isolated failure toggling within one test scenario.

💡 Solution & Code Walkthrough
page.route() enables dynamic network interception. We'll use a failProductService flag to switch ProductService responses between success, HTTP 500, and network timeout.

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

let failProductService = false;

test.beforeEach(async ({ page }) => {
  await page.route('**/api/products', async (route) => {
    if (failProductService) {
      const errorType = Math.random() < 0.5 ? '500' : 'network';
      if (errorType === '500') {
        await route.fulfill({
          status: 500,
          contentType: 'application/json',
          body: JSON.stringify({ message: 'Product service unavailable' }),
        });
      } else {
        await route.abort('failed');
      }
    } else {
      await route.continue();
    }
  });
});

test('should handle dynamic product service failures gracefully', async ({ page }) => {
  await page.goto('/your-app-path');

  // 1. Simulate ProductService failure (e.g., 500/timeout)
  failProductService = true;
  await page.reload();
  await expect(page.getByText(/product.*unavailable|failed to load products/i)).toBeVisible();
  // ... assert UI state (error message, retry button)

  // 2. Simulate ProductService recovery
  failProductService = false;
  await page.getByRole('button', { name: 'Retry' }).click(); // Assuming a retry button
  await expect(page.getByText('Loading Products...')).not.toBeVisible();
  await expect(page.getByTestId('product-list')).toBeVisible(); // Verify products loaded
});
Enter fullscreen mode Exit fullscreen mode

page.route() isolates /api/products interception.
failProductService dynamically toggles backend states.
❌ Avoid static stubs for realistic intermittency.

🔑 Key Takeaways
page.route() offers granular network control for UI testing.
• Dynamic flags within page.route() simulate realistic, intermittent backend issues.
• This approach validates UI resilience and error handling without backend changes.

❓ Quick Summary Q&A
Dynamic failures? Use page.route() with a mutable flag (failProductService) to control responses (500, abort, continue).
Why page.route()? Enables dynamic state changes for realistic, intermittent failure/recovery tests.

TAGS: playwright, e2e testing, ui testing, test automation, service virtualization, frontend testing, network mocking
────────────────────────────────────────
👉 Elevate your testing skills! For more advanced Playwright strategies and interview prep, download our app:

────────────────────────────────────────

📲 𝐅𝐑𝐄𝐄 𝐌𝐎𝐁𝐈𝐋𝐄 𝐀𝐏𝐏 — 𝟔𝟎𝟎+ 𝐒𝐃𝐄𝐓 𝐐&𝐀𝐬
Practice real-world interview scenarios offline on the free QA Automation & SDET Prep app:

🤖 𝐆𝐨𝐨𝐠𝐥𝐞 𝐏𝐥𝐚𝐲 (𝐀𝐧𝐝𝐫𝐨𝐢𝐝):
https://play.google.com/store/apps/details?id=com.app.seleniuminterviewquestions&referrer=utm_source%3Ddevto%26utm_medium%3Darticle%26utm_campaign%3Dselenium_20260916

🍎 𝐀𝐩𝐩 𝐒𝐭𝐨𝐫𝐞 (𝐢𝐎𝐒):
https://apps.apple.com/app/id6786760948?pt=128640464&ct=devto_selenium_20260916&mt=8

────────────────────────────────────────
────────────────────────────────────────

Top comments (0)