DEV Community

Styrow.dev
Styrow.dev

Posted on Originally published at styrow.dev

Designing Robust Session Management in Distributed Playwright Tests

🤯 Playwright Distributed Test Challenge:
Are your distributed Playwright tests battling shared state and flaky cleanups? Learn to design robust, isolated sessions.


📌 Problem Statement
Large-scale Playwright test suites often run concurrently across multiple workers. When these tests interact with shared, stateful backend resources (e.g., Kafka topics, temporary databases), ensuring each worker has a unique, clean session becomes critical.

❌ Relying on global state or manual cleanup is unreliable.
❌ Mid-test failures or worker crashes can leave orphaned resources, causing flakiness.
✅ Each worker needs an isolated context, guaranteed setup, and bulletproof cleanup.

💡 Solution & Code Walkthrough
Playwright's test.extend provides powerful fixture capabilities for atomic resource acquisition and guaranteed cleanup. We'll create a custom fixture that manages a unique session ID and associated resources.

// tests/fixtures.ts
import { test as baseTest, BrowserContext } from '@playwright/test';

// Define a type for our extended fixtures
type MyFixtures = {
  // A unique ID for the current test session
  sessionId: string;
  // A Playwright browser context configured with the unique ID
  sessionContext: BrowserContext;
};

// Extend the base Playwright test object
export const test = baseTest.extend<MyFixtures>({
  // sessionId fixture definition
  sessionId: [async ({}, use) => {
    // ✅ 1. Acquire unique, ephemeral ID before tests start
    const uniqueId = `session-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`;
    console.log(`Worker acquiring session: ${uniqueId}`);

    await use(uniqueId); // Provide the uniqueId to the test

    // ✅ 2. Guaranteed cleanup after the test completes (or fails)
    console.log(`Worker cleaning up session: ${uniqueId}`);
    // Simulate cleanup of backend resources associated with uniqueId
    await new Promise(resolve => setTimeout(resolve, 50)); // Placeholder for actual API call
  }, { scope: 'worker', auto: true }], // 'worker' scope for per-worker isolation

  // sessionContext fixture definition, depends on sessionId
  sessionContext: [async ({ browser, sessionId }, use) => {
    // ✅ 3. Configure BrowserContext with unique ID
    const context = await browser.newContext({
      extraHTTPHeaders: {
        'X-Session-ID': sessionId, // Inject unique ID via header
      },
    });
    await context.addCookies([{ name: 'session_id', value: sessionId, url: 'http://localhost' }]);

    await use(context); // Provide the configured context

    // 4. Ensure context is closed after test
    await context.close();
  }, { scope: 'worker' }], // 'worker' scope for per-worker isolation
});

// To use in tests:
// import { test } from './fixtures';
// test('my distributed test', async ({ sessionContext, sessionId, page }) => {
//   // Use sessionContext or sessionId here
//   await sessionContext.newPage();
//   // ... test logic ...
// });
Enter fullscreen mode Exit fullscreen mode

test.extend with scope: 'worker' guarantees sessionId and sessionContext are unique and managed per parallel worker.
• The use() function's parameter in a fixture is what's passed to the test.
• The code after await use() acts as the teardown logic, guaranteed to run.

🔑 Key Takeaways
Isolation: Each worker gets a dedicated sessionId and sessionContext, preventing cross-contamination.
Reliability: Fixture teardown logic (code after await use()) always runs, ensuring cleanup even if a test fails.
Scalability: This pattern scales effortlessly in distributed environments, as each worker manages its own lifecycle.

Quick Summary Q&A
Q: How do I ensure unique resources per distributed worker?
A: Use test.extend fixtures with scope: 'worker' to generate and manage unique IDs or objects per worker.
Q: What guarantees resource cleanup?
A: Any code placed after await use() within a fixture's setup function is the guaranteed teardown, executed even upon test failure.

TAGS: playwright, testing, distributed testing, test automation, javascript, typescript, sdet, qa

────────────────────────────────────────
Download our app for more exclusive Playwright insights and coding challenges!
────────────────────────────────────────

📲 𝐅𝐑𝐄𝐄 𝐌𝐎𝐁𝐈𝐋𝐄 𝐀𝐏𝐏 — 𝟔𝟎𝟎+ 𝐒𝐃𝐄𝐓 𝐐&𝐀𝐬
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_20260905

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

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

Top comments (0)