Playwright for Real-Time Applications: Testing WebSockets and Live Data Streams
Use Playwright to reliably test real-time apps and capture WebSocket/SSE events, handle dynamic updates, simulate users, and catch edge cases for robust automation.
Join the DZone community and get the full member experience.
Join For FreeReal-time applications are everywhere these days. From chat apps to stock dashboards and collaborative tools, users expect instant feedback.
If you’ve ever tried to test these apps, you know the challenge: traditional automation just doesn’t cut it. The page doesn’t reload, elements appear and disappear unpredictably, and multiple users can interact simultaneously.
That’s where Playwright shines. Unlike older tools, it’s built to handle dynamic, real-time web applications, letting you intercept network events, track WebSocket messages, and assert UI changes almost like a real user.
In this blog, I’ll walk you through practical strategies for testing real-time apps with Playwright, based on real-world experience, and end with a step-by-step testing checklist you can use immediately.
Why Real-Time Apps Can Be Tricky
When I first started testing live applications, I quickly realized they’re a different beast. Here’s why:
- Data updates constantly – Stock tickers, chat messages, or notifications pop up without page reloads.
- Events happen asynchronously – One action can trigger multiple updates elsewhere.
- Race conditions and latency – Messages might arrive out of order.
- UI elements change dynamically – Buttons, messages, or dashboards can update without user input.
- Multiple users – Changes by one user must reflect for all others in real time.
Without a smart approach, automation becomes flaky and unreliable.
Why Playwright Works So Well
Playwright makes testing real-time apps easier with:
- Network interception – Capture WebSocket frames, SSE messages, and API calls in real time.
- Event listeners – React to DOM or network events as they happen.
- Dynamic assertions – Automatic retries ensure your tests pass only when the expected state is reached.
- Multi-user simulation – Multiple browser contexts mimic real users interacting simultaneously.
- Timeouts and retries – Avoid test failures caused by minor network delays or slow updates.
Testing WebSockets: A Real Example
A chat app is a classic real-time scenario. Here’s how I approach it:
import { test, expect } from '@playwright/test';
test('Chat messages appear correctly for all users', async ({ page }) => {
await page.goto('https://example-chat-app.com');
page.on('websocket', ws => {
ws.on('frameReceived', frame => {
console.log('WebSocket message received:', frame.payload);
});
});
await page.fill('#chatInput', 'Hello from Playwright!');
await page.click('#sendBtn');
await expect(page.locator('#chatMessages')).toContainText('Hello from Playwright!');
});
Here we:
- Intercept WebSocket messages.
- Send a chat message.
- Verify it appears in the UI.
Playwright retries the assertion until the message appears, handling minor network delays seamlessly.
Validating Live Data Feeds
For dashboards or financial tickers:
await expect(page.locator('.stock-price')).toHaveText(/^\$\d+\.\d{2}$/);
The automatic retry ensures that fluctuating values don’t cause flaky tests.
Multi-User Testing
const [user1, user2] = await Promise.all([
browser.newPage(),
browser.newPage()
]);
await user1.goto('https://example-chat-app.com');
await user2.goto('https://example-chat-app.com');
await user1.fill('#chatInput', 'Hi there!');
await user1.click('#sendBtn');
await expect(user2.locator('#chatMessages')).toContainText('Hi there!');
This approach confirms that messages sent by one user appear correctly for another, which is essential in collaborative apps.
SSE and Streaming APIs
Some apps push updates via SSE or streaming endpoints. Playwright can handle these too:
page.on('response', async response => {
if (response.url().includes('/streaming-endpoint')) {
const data = await response.text();
console.log('Streaming data:', data);
}
});
Monitoring the raw data helps verify that the backend is sending correct updates, even before the UI refreshes.
Real-World Tips for Reliable Testing
1. Focus on Relevant Events Only
When testing WebSockets or Server-Sent Events, it’s tempting to monitor every single message. But this often creates noise, slows down tests, and makes debugging frustrating.
Best practice: Identify the critical events your feature depends on. For example:
- Chat apps: new message events, typing indicators, and user presence updates.
- Dashboards: price updates, metric refreshes, alert triggers.
page.on('websocket', ws => {
ws.on('frameReceived', frame => {
if (frame.payload.includes('newMessage')) {
console.log('New message event received:', frame.payload);
}
});
});
This ensures your tests are focused, easier to maintain, and faster to debug.
2. Use waitForResponse
or waitForEvent
Strategically
Real-time data doesn’t always arrive instantly. If your assertions run too early, tests can fail even though the app works correctly. Playwright’s waitForResponse
and waitForEvent
allow you to synchronize your test with the application state.
Example scenario: Testing a stock dashboard:
- A new price update may take a few milliseconds to reach the client.
- Using
waitForResponse
, you can pause your assertion until the relevant data arrives
await page.waitForResponse(response =>
response.url().includes('/stock-price-stream') && response.status() === 200
);
This approach reduces flaky tests caused by timing issues.
3. Simulate Real User Behavior
Automation isn’t just about code — it’s about emulating real users. Tests become more reliable when you consider natural user actions:
- Typing messages with delays instead of filling input instantly.
- Opening multiple sessions to test multi-user interactions.
- Simulating reconnections for users who go offline and return.
Real example: While testing a chat app, we discovered that offline users weren’t receiving messages after reconnecting. By simulating reconnections in Playwright, we caught this issue before production.
4. Retry Dynamic Assertions
Live applications update frequently. A dashboard element might refresh several times in milliseconds. Playwright’s expect
with automatic retries ensures your test waits for the correct state rather than failing immediately.
await expect(page.locator('.notification')).toHaveText('Trade executed successfully');
Even if the notification appears a few milliseconds late, Playwright retries the assertion until it succeeds or the timeout is reached.
5. Log Network and UI Events for Debugging
When real-time tests fail, understanding why is often difficult. Logging is essential:
- Record WebSocket frames or SSE messages.
- Capture screenshots or snapshots when assertions fail.
- Include timestamps to see the sequence of events.
page.on('websocket', ws => ws.on('frameReceived', frame => console.log(Date.now(), frame.payload)));
This makes it much easier to trace failures and understand the exact flow of events.
6. Test Edge Cases and Failures
Real-time apps can break in unusual ways. A robust test suite covers:
- Network delays – Simulate slow connections to see how the app behaves.
- Dropped messages – Ensure the UI handles missing updates gracefully.
- High-frequency updates – Test if the interface can keep up without breaking.
Scenario: While testing a live feed with 50+ updates per second, we discovered that some UI elements lagged behind. Catching this early allowed the development team to optimize rendering before it reached users.
Step-by-Step Playwright Real-Time Testing Checklist
Here’s a practical checklist I use when testing real-time applications. You can follow it for any WebSocket, SSE, or live data scenario:
- Identify critical real-time flows – Chat messages, notifications, live charts, or collaborative actions.
- Capture relevant network traffic – WebSocket frames, SSE messages, or streaming endpoints.
- Validate backend data – Confirm the server is sending correct payloads.
- Verify UI updates – Ensure the frontend reflects backend changes accurately.
- Simulate multiple users – Test interactions between two or more users to catch concurrency issues.
- Add dynamic assertions – Use
expect
with retries for fluctuating data. - Test edge cases – Network delays, message loss, or rapid-fire updates.
- Log everything – Save network messages, UI updates, and errors for debugging.
- Run tests in CI/CD – Automate tests in headless mode with parallel execution.
- Review flaky tests – Analyze failures and improve timing or assertions to make them reliable.
Wrapping Up
Testing real-time applications might seem intimidating at first, but it becomes manageable once you understand the flow. With Playwright, you can:
- Validate WebSocket communications
- Monitor live data feeds
- Simulate multiple users interacting concurrently
By combining network monitoring, event listening, and dynamic assertions, you can catch subtle bugs before they reach your users. Think like a real user: observe what happens in the UI, confirm what’s happening behind the scenes, and automate it with Playwright.
Following the checklist above gives you a repeatable, human-friendly testing process that reduces flakiness and increases confidence in real-time features.
Real-time apps don’t have to be a testing headache anymore — Playwright gives you the tools to test them like a pro.
Opinions expressed by DZone contributors are their own.
Comments