WebSocket Testing
WebSockets enable real-time bidirectional communication — live dashboards, notifications, chat, collaborative editing. They behave nothing like REST. Standard API testing tools don’t work. This entry teaches you to test WebSocket connections, message formats, and real-time behaviour.
1 The Hook
A Wellington logistics company builds a live shipment tracking dashboard. The API tests all pass. The data is correct. They go live.
The project manager notices the dashboard updates only on page refresh — not in real time as the spec promised. Investigation reveals: the WebSocket connection drops silently after 30 seconds due to a server idle timeout misconfiguration. Every 30 seconds, the connection dies and the client never reconnects.
The REST tests had tested the data correctly. They never tested the WebSocket connection that should have been pushing the updates. No WebSocket tests existed. A feature that was core to the product’s value proposition had shipped with a silent failure mode that no test had ever touched.
The fix took two hours. The missed test took months to recover from commercially.
Every team I've seen discovers this the hard way: testing that WebSocket messages arrive is not WebSocket testing. The bugs that kill real-time features in production are never in the message payload — they're in the load balancer. AWS ALB defaults to a 60-second idle timeout. NGINX ships with 60 seconds too. Cloudflare drops idle WebSockets at 100 seconds. In NZ government and banking projects, these reverse proxies sit silently between your perfectly working application and your users, and they kill the connection with a 1006 close code — no close frame, no visible error, just a frozen dashboard. The client has no idea. The REST tests all pass. Your message schema tests all pass. And every 60 seconds, real users stop getting updates. Test idle timeout explicitly against your actual production proxy configuration — not localhost, not staging-without-a-proxy. That is the test almost no one writes until after the incident.
Senior engineer insight
The most dangerous assumption in WebSocket testing is that "it worked in dev" means it will work in production. What changes in production is the infrastructure between your app and the browser: load balancers, reverse proxies, and CDN edges all have their own WebSocket timeout rules that have nothing to do with your application code. Once I started treating the proxy configuration as part of the system under test — not background infrastructure — my WebSocket bug detection rate doubled overnight.
Most common mistake: teams write tests that assert on message content and call it done, never touching idle timeout, reconnection, or close codes — so the connection lifecycle is completely untested until it fails in production at 2am.
From the field
On a Wellington fintech project we were building a live foreign exchange rate dashboard — traders needed sub-second updates on NZD/USD and NZD/AUD. We had solid message schema tests and everything looked green. Go-live was smooth. Three days later a trader reported that rates were "freezing" mid-morning, always around the same time. The WebSocket tests all passed. It took us a day to trace it back to the AWS ALB idle timeout: during a quiet period in the NZD market, no messages flowed for just over 60 seconds, the load balancer silently dropped the connection, and the client had no reconnection logic. The lesson that generalises: always test what happens when nothing happens — silence is its own failure mode in real-time systems, and the fix (add a server-side ping every 30 seconds plus client-side reconnect logic) is trivial once you know where to look.
2 The Rule
WebSocket tests must verify three things independently: connection establishment, message format (both directions), and connection lifecycle — idle timeout, reconnection, and disconnect behaviour. Testing only the REST API behind a real-time feature is not WebSocket testing.
3 The Analogy
Testing a WebSocket by testing the REST API is like testing a phone call by reading the phone book.
The data is right — you have the number — but you have not tested whether the call connects, whether both parties can hear each other, or whether the line drops after 30 minutes of silence. The phone book is correct. The phone call is a completely different thing.
WebSocket is a persistent, bidirectional protocol running over a separate connection from HTTP. REST tests tell you nothing about it. You need a different test for a different thing.
4 Watch Me Do It
Playwright has built-in WebSocket event listeners. Use them to capture and assert on frames without any additional tooling.
Test reconnection after a network interruption:
Manual testing tools
- wscat —
npm install -g wscatthenwscat -c ws://localhost:3000/tracking. Connect to any WebSocket from the terminal and send/receive frames manually. - Postman — Collection › New › WebSocket Request. Good for exploring message schemas and testing auth headers.
- Browser DevTools — Network tab › WS filter. Shows all frames, connection timing, and close codes without any extra tooling.
5 When to Use It
Any feature using real-time communication needs WebSocket-specific tests. Common NZ examples: live auction bidding, banking transaction notifications, freight tracking dashboards, collaborative document editing, real-time support chat.
Your WebSocket test plan must cover all of these independently:
- Connection establishment — does the client connect on page load?
- Message schema validation — do received messages match the expected JSON shape?
- Message delivery latency — does the update arrive within an acceptable window?
- Heartbeat / ping-pong — does the client respond to server pings to keep the connection alive?
- Idle timeout — what happens after 30–60 seconds with no messages?
- Reconnection — does the client reconnect after a drop, and how quickly?
- Error messages — does the server send meaningful error frames for bad requests?
- Multi-client isolation — does one client’s disconnect affect others?
6 Common Mistakes
🚫 Assuming REST API coverage covers the real-time feature
What I used to think: If the REST API returns the right data, the real-time feature works.
Actually: WebSocket is a separate protocol over a separate persistent connection. REST tests tell you nothing about WebSocket connection establishment, message delivery, idle timeout behaviour, or reconnection logic. The logistics company in the hook had correct REST tests and a broken real-time feature simultaneously.
🚫 Thinking WebSocket testing requires specialist tools
What I used to think: WebSocket testing is hard and needs expensive tools.
Actually: Playwright has built-in WebSocket event listeners that work out of the box. For manual exploration, wscat (npm install -g wscat) lets you connect to any WebSocket from the terminal. For contract testing, you can assert on JSON message schemas directly in your Playwright tests.
🚫 Only testing that messages arrive, not the connection itself
What I used to think: I only need to check that the UI updates when messages come in.
Actually: WebSocket connection issues — idle timeouts, dropped connections without reconnect, memory leaks from unclosed connections — are the most common production failures in real-time features. The connection lifecycle is a first-class test concern. Test it explicitly and separately from message content.
7 Industry Reality
- WebSocket coverage is almost always an afterthought. Most teams write REST tests first, ship the feature, and only add WebSocket tests after something breaks in production. If you join a project with real-time features and zero WS tests, you are not the first.
- The spec rarely documents the full lifecycle. You will frequently get "the WebSocket should push updates in real time" and nothing about idle timeout values, reconnection strategy, close codes, or what happens when the server restarts mid-session. You have to dig into the server code or wscat experiments to discover the actual behaviour.
- Senior testers check close codes, not just message content. Textbook answers focus on asserting message payload. On real projects, the most common failures are close code 1006 (abnormal closure), connections not reconnecting after a server deploy, and memory leaks from connections that are opened but never explicitly closed across page navigation.
- NZ infrastructure adds latency constraints that matter. Real-time features serving NZ customers often use offshore WebSocket servers (Australia, US West Coast). A 500ms latency threshold that passes in staging will fail intermittently in production from Hamilton or Dunedin. Test against realistic latency, not localhost.
- Load balancers and reverse proxies break WebSocket silently. AWS ALB, NGINX, and Cloudflare all have different WebSocket idle timeout defaults. A feature that works in dev (no proxy) will silently drop connections in production after 60 seconds. This is one of the most common real-world WebSocket bugs and you will likely never see it mentioned in a ticket — you find it by observing production logs.
8 When to Use It — and When Not To
✓ Use it when
- The feature uses a persistent WebSocket connection to push data to the UI (live dashboards, notifications, chat, collaborative editing, auction bidding, freight tracking)
- The business requirement mentions "real-time", "live updates", or "without refreshing the page" — those words mean WebSocket tests are required
- The system must handle reconnection after a network drop — you need explicit lifecycle tests, not just message content tests
- Multiple clients share the same WebSocket channel — isolation failures (one client's disconnect affecting others) are real bugs and need explicit tests
- The app runs behind a load balancer or reverse proxy in production — idle timeout configuration bugs will only surface with lifecycle tests
✗ Skip it when
- The feature uses polling (the client calls the REST API every N seconds to check for updates) — that is just REST testing, no WebSocket involved
- The feature uses Server-Sent Events (SSE) rather than WebSocket — SSE is unidirectional HTTP streaming and requires a different approach
- The WebSocket is only used for a minor enhancement that has a graceful REST fallback — focus effort on the REST path first and add WS tests only if it ships to production
- You are testing a third-party WebSocket library or provider (e.g. Pusher, Ably) — test your usage of the service, not the service itself
- Time pressure forces triage — if you can only test one thing, test that the feature degrades gracefully when the WebSocket is unavailable rather than testing the happy path
Context guide
How the right level of WebSocket testing effort changes based on project context.
| Context | Priority | Why |
|---|---|---|
| Financial services real-time data (Harbour Bank, Southern Bank KiwiSaver dashboards, NZX trading feeds) | Essential | Regulatory obligations around accurate real-time disclosure mean stale or dropped data is a compliance failure, not just a UX issue. Connection lifecycle and idle-timeout tests are mandatory before go-live. |
| Government citizen portals (Benefits NZ case management, Revenue NZ myIR notifications, CoverNZ claims tracker) | Essential | Users on rural mobile connections (Northland, Southland) experience frequent brief drops. Silent freeze without reconnection is a serious accessibility and service-delivery risk. Reconnection SLA tests are non-negotiable. |
| Logistics and freight tracking (Mainfreight, Post Haste, PostNZ parcel dashboards) | High | Idle-timeout failures are highly likely — freight status updates are bursty, with long quiet periods. Test explicitly for 60-second idle survival behind AWS ALB. Message schema tests confirm status code fidelity. |
| Internal tooling (Spark, LedgerNZ, TransitNZ internal dashboards) | Medium | Users are staff, not customers — a manual refresh workaround is tolerable for a short period. Cover connection establishment and critical message types; defer load-at-scale and multi-region latency tests unless SLAs demand them. |
| Marketing or content sites with live feed widgets (news tickers, live event scores) | Low | Stale content is a cosmetic issue, not a business-critical failure. Verify the connection opens and a sample message arrives; skip lifecycle and reconnection testing unless the feed drives business decisions. |
Trade-offs
What you gain and what you give up when you choose WebSocket testing.
| Advantage | Disadvantage | Use instead when… |
|---|---|---|
| Catches silent lifecycle failures (idle timeout, close code 1006, no-reconnect) that REST and UI tests completely miss | More complex to set up than REST tests — requires async frame-collection patterns (expect.poll), fresh browser contexts per test, and explicit teardown to avoid connection leaks |
The feature uses polling (REST calls every N seconds) — standard HTTP testing is sufficient and simpler |
| Validates both directions of communication in a single test — you can assert on what the client sends and what the server pushes back, covering the full interaction loop | Tests are environment-sensitive — idle-timeout tests must run against the actual production proxy configuration, not localhost, or they give false confidence | The feature uses Server-Sent Events (SSE) — unidirectional HTTP streaming tested with standard HTTP response assertions is simpler and correct |
Provides measurable latency SLAs — expect.poll with a millisecond timeout gives you a repeatable, CI-enforceable assertion on real-time delivery performance |
Flakiness risk is higher than REST tests — async frame delivery timing varies under CI load; latency assertions require careful threshold calibration per environment | You are testing a third-party WebSocket provider (e.g. Pusher, Ably) — test your own usage contract, not the provider's infrastructure reliability |
| Multi-client isolation tests expose race conditions and channel leakage that no other test type surfaces — critical for auction, chat, and collaborative editing features | Lifecycle tests (idle timeout, reconnection) are slow by nature — a 60-second idle test cannot be shortened without defeating its purpose; keep them in a separate nightly suite, not the PR pipeline | Time pressure forces triage — prioritise testing graceful degradation when WebSocket is unavailable over the happy-path message delivery test |
Enterprise reality
How WebSocket testing changes when you are operating across 200–300 developers, multiple product squads, and live financial or government systems at NZ enterprise scale.
- At small-team scale, WebSocket testing is manual and exploratory — wscat sessions, one-off Playwright scripts, checking DevTools close codes. At enterprise scale (10+ squads sharing a WebSocket gateway), this is automated and mandatory: connection lifecycle tests run in CI on every PR, idle-timeout thresholds are codified as shared test fixtures, and a dedicated platform team owns the WebSocket infrastructure layer so product squads don’t silently misconfigure it.
- Harbour Bank’s open banking platform is subject to the Consumer Data Right (CDR) regime and the Privacy Act 2020: real-time data pushed over WebSocket connections must meet strict data-minimisation and consent obligations. At enterprise scale, this means automated schema-validation tests that verify no PII fields appear in frames unless consent is confirmed — a compliance gate, not an optional QA step.
- Enterprise tooling choices diverge sharply from small-team defaults: Playwright suffices for functional connection tests, but load and soak testing at 50,000 concurrent WebSocket connections requires k6 (with its
wsmodule) or Artillery — both integrate with Grafana dashboards so SRE teams can correlate connection drop rates with infrastructure metrics in real time. Postman’s WebSocket support is adequate for exploratory work but not for CI-scale automation. - When 10+ squads push features concurrently, a WebSocket regression in one squad’s service can cascade across shared channels — a single misconfigured idle timeout in KiwiFirst Bank’s notification gateway, for instance, could silence real-time payment confirmations for all connected clients simultaneously. Enterprise teams address this with contract testing (Pact) between WebSocket producers and consumers, and a shared WebSocket SLA registry that defines timeout, reconnection, and latency thresholds per channel — enforced automatically in the release pipeline, not left to individual squad interpretation.
◆ What I would do
Professional judgment — when to reach for WebSocket testing, when to skip it, and what to watch for.
I am joining an Harbour Bank online banking project mid-sprint. The team has a live FX rate dashboard that “feels sluggish.” REST tests are green. No WebSocket tests exist.
Open Chrome DevTools, filter the Network tab by WS, and watch the close code when the page sits idle for 90 seconds. If I see a 1006 close code with no close frame, I have found the bug without writing a single line of test code. Then I write three targeted tests: (1) connection establishment on page load, (2) a rate update message arrives within 2 seconds of being published, and (3) the connection survives a 90-second idle period. I do not write load tests or multi-region latency tests in the same sprint — those are separate work items. Fix the most commercially dangerous gap first.
I am writing the test plan for Benefits NZ’s new case status notification feature — clients see real-time updates on their case progress via a WebSocket-powered portal. The team asks whether to include reconnection tests in the sprint.
Yes, include them — unconditionally. Benefits NZ’s users are disproportionately likely to be on congested mobile networks in areas with patchy coverage. A frozen portal with no error message is a welfare-access failure, not just a UX annoyance. I would write a reconnection test using page.context().setOffline(true/false) that asserts reconnection within 8 seconds — a realistic SLA for a government portal. I would also add a visible “reconnecting…” indicator test, because silent failures erode trust in a population that already has low trust in digital government services.
I am on a Spark internal ops tool project. The product owner pushes back on WebSocket lifecycle tests, saying “it’s internal staff, they can just refresh the page.” We are two days from sprint close.
Accept the triage call for now — internal tools with a tolerant user base and a visible refresh button genuinely do have lower reconnection risk than a customer portal. I write the connection establishment test and one message schema test, log a backlog ticket for reconnection and idle-timeout tests with the risk clearly documented, and move on. The key discipline here is that “skip it now” is only acceptable when it is a conscious, logged decision — not an invisible gap. If the idle-timeout bug surfaces in production six months later, the backlog ticket demonstrates the team made an informed risk call, not that testing was overlooked.
The bottom line: WebSocket bugs do not fail fast or fail loudly — they freeze silently, hours after everything looked green. Write lifecycle tests before go-live on any customer-facing real-time feature; log an explicit risk when you defer them on internal tooling.
9 Best Practices
- ✓ Test connection, messages, and lifecycle as three separate concerns. Write a dedicated connection test that just asserts the WebSocket opens on page load. Write separate message tests. Write separate lifecycle tests (idle timeout, reconnection, disconnect). Do not bundle all three into one monolithic test.
- ✓ Use
expect.poll()for all async assertions — neverwaitForTimeout. Fixed sleeps create flaky tests and slow suites.expect.poll(() => messages.length > 0, { timeout: 3000 })exits the moment the condition is true and fails with a clear message if it times out. - ✓ Capture and log close codes on every WebSocket. Attach a
ws.on('close', () => ...)handler in your test setup and record the close code. Code 1000 is a normal close. Code 1006 is abnormal closure (server dropped the connection without a close frame). This is the most common silent failure mode in production. - ✓ Use separate browser contexts to test multi-client isolation. To verify one client’s disconnect does not affect others, create two
browser.newContext()instances and give each its own WebSocket listener. This reflects the actual architecture — each real user has a separate connection. - ✓ Validate the full message schema, not just a field existence check. Assert field names, types, and value ranges. A schema that returns
bidAmount: "1500"(string) instead ofbidAmount: 1500(number) will break client-side calculations silently. - ✓ Test with realistic latency where real-time SLAs matter. If the spec says “updates within 500ms”, run latency tests from a CI agent in the same region as your users — not just localhost. NZ users on Australian-hosted infrastructure routinely see 30–80ms base RTT; that changes what latency thresholds you can promise.
- ✓ Use wscat for exploratory testing before writing automation.
wscat -c wss://your-app.co.nz/wsfrom the terminal gives you an interactive session to probe the server, discover the actual message schema, and test auth behaviour before you commit to writing test code. - ✓ Check the browser DevTools WS tab before raising a bug. Filter the Network tab by WS, select the connection, and read the Frames panel. It shows every frame with timestamps, direction, and payload — this takes 30 seconds and eliminates half of “WebSocket is broken” misdiagnoses.
- ✓ Explicitly close connections in test teardown. Unclosed WebSocket connections from a test suite accumulate and can affect server-side connection limits. Use
page.close()orcontext.close()inafterEachhooks rather than relying on implicit cleanup. - ✓ Document the expected close code for your app in the test as a comment. When a test asserts on reconnection behaviour, a comment explaining “close code 1001 = server restart, triggers automatic reconnect” saves the next tester 30 minutes of digging through RFC 6455.
10 Common Misconceptions
❌ Myth: If the REST API tests pass, the real-time WebSocket feature is covered.
Reality: REST and WebSocket are completely separate protocols running over separate connections. A REST test confirms that the server stores and returns the right data. It says absolutely nothing about whether the WebSocket connection opens, whether messages are pushed to connected clients, whether the connection survives an idle period, or whether the client reconnects after a drop. You can have 100% REST test coverage and a completely broken real-time feature at the same time — as the hook story illustrates.
❌ Myth: WebSocket testing requires expensive specialist tools or a dedicated WS testing framework.
Reality: Playwright has built-in WebSocket event listeners that work out of the box with zero additional dependencies — page.on('websocket', ...) is part of the standard API. For manual exploration, wscat is free, installs in 10 seconds, and runs from any terminal. For schema validation, you can assert on parsed JSON directly in your Playwright tests. The tooling is accessible to any team already using Playwright.
❌ Myth: A WebSocket message arriving in the UI proves the connection is healthy and stable.
Reality: A single successful message confirms one frame was delivered — it says nothing about long-term connection stability. The most common production WebSocket failures (idle timeouts after 30–60 seconds, connections dropped by load balancers, clients that open a new connection on every navigation without closing the old one) only manifest over time or at scale. You need explicit lifecycle tests — idle timeout, reconnection, disconnect isolation — that run separately from message content tests.
11 Now You Try
Write your answer, run it for AI feedback, then check the model answer.
A NZ real-time auction platform uses WebSockets to push bid updates to all participants. Write 4 test cases: (1) a bid update message is received within 500ms of being placed, (2) the message schema is correct (bidAmount, bidderId, timestamp), (3) the connection reconnects within 5 seconds after a network drop, and (4) disconnecting one participant does not affect other participants’ connections.
Show model answer
Test 1 — Bid update within 500ms:
const messages: string[] = [];
page.on('websocket', ws => ws.on('framereceived', f => messages.push(f.payload as string)));
await page.goto('/auction/NZ-LOT-042');
const start = Date.now();
await page.request.post('/api/bids', { data: { lotId: 'NZ-LOT-042', amount: 1500 } });
await expect.poll(() => messages.some(m => m.includes('NZ-LOT-042')), { timeout: 500 }).toBeTruthy();
expect(Date.now() - start).toBeLessThan(500);
Test 2 — Message schema validation:
const received: any[] = [];
page.on('websocket', ws => ws.on('framereceived', f => {
try { received.push(JSON.parse(f.payload as string)); } catch {}
}));
await page.goto('/auction/NZ-LOT-042');
await page.request.post('/api/bids', { data: { lotId: 'NZ-LOT-042', amount: 1500 } });
await expect.poll(() => received.length > 0, { timeout: 2000 }).toBeTruthy();
const msg = received[received.length - 1];
expect(msg).toHaveProperty('bidAmount');
expect(msg).toHaveProperty('bidderId');
expect(msg).toHaveProperty('timestamp');
expect(typeof msg.bidAmount).toBe('number');
expect(typeof msg.bidderId).toBe('string');
expect(Date.parse(msg.timestamp)).not.toBeNaN();
Test 3 — Reconnection within 5 seconds:
let connections = 0;
page.on('websocket', () => connections++);
await page.goto('/auction/NZ-LOT-042');
expect(connections).toBe(1);
await page.context().setOffline(true);
await page.waitForTimeout(1000);
await page.context().setOffline(false);
await expect.poll(() => connections >= 2, { timeout: 5000, message: 'Should reconnect within 5s' }).toBeTruthy();
Test 4 — Disconnect isolation:
// Use two browser contexts to simulate two participants
const context2 = await browser.newContext();
const page2 = await context2.newPage();
let page2Messages: string[] = [];
page2.on('websocket', ws => ws.on('framereceived', f => page2Messages.push(f.payload as string)));
await page.goto('/auction/NZ-LOT-042');
await page2.goto('/auction/NZ-LOT-042');
// Close page1 (participant 1 disconnects)
await page.close();
// Participant 2 should still receive bid updates
await page2.request.post('/api/bids', { data: { lotId: 'NZ-LOT-042', amount: 1600 } });
await expect.poll(() => page2Messages.some(m => m.includes('1600')), { timeout: 3000 }).toBeTruthy();
await context2.close();
Why teams fail here
- Treating REST test coverage as proof the real-time feature works — the two protocols are completely independent and REST tests cannot detect WebSocket failures
- Never testing idle timeout — load balancers (AWS ALB, NGINX, Cloudflare) all have their own WebSocket timeout defaults that silently kill connections after 60–100 seconds of inactivity
- Using fixed
waitForTimeoutsleeps instead ofexpect.poll()— creates slow, flaky tests that mask real timing problems rather than surfacing them - Skipping reconnection tests entirely — "the user can just refresh" is not an acceptable answer for live dashboards, auction platforms, or any NZ system with real-time regulatory or commercial obligations
Key takeaway
WebSocket testing is not about whether messages arrive — it is about whether the connection survives everything the network and infrastructure can throw at it between your server and your user.
How this has changed
The field moved. Here is how WebSocket Testing evolved from its origins to current practice.
Real-time web communication requires polling or long-polling — inefficient hacks that abuse HTTP. Testing real-time behaviour means testing polling intervals and response timing. True bidirectional communication over a single connection does not exist on the web.
RFC 6455 standardises WebSocket protocol. Adoption grows rapidly with Socket.io, SignalR, and native browser WebSocket support. Testing WebSocket applications requires tools that understand the persistent connection model — HTTP testing tools cannot test WebSocket frames.
Postman adds WebSocket support. WSTool and Chrome DevTools WebSocket inspection enable manual debugging. Automated WebSocket testing lags behind REST testing maturity by several years.
Playwright, Cypress, and k6 add WebSocket testing support. Automated WebSocket testing becomes part of the standard CI test suite for real-time applications — chat, collaborative editing, live dashboards, and financial data feeds.
WebSocket testing covers: connection establishment under load, message ordering guarantees, reconnection handling, authentication token refresh during long sessions, and graceful degradation when WebSocket is unavailable. AI applications increasingly use WebSocket streaming for token-by-token output delivery — requiring testing of streaming response completeness and error handling.
12 Self-Check
Click each question to reveal the answer.
Interview Questions
What NZ hiring managers ask about WebSocket Testing — and what strong answers look like.
What is the lifecycle of a WebSocket connection, and what are the key test points at each stage?
Strong answer: Establishment: HTTP Upgrade request succeeds (101 Switching Protocols), correct headers (Sec-WebSocket-Accept), and authentication is validated before the upgrade. Open: bidirectional messages are delivered in order, messages are correctly serialised/deserialised, and concurrent messages from multiple clients do not interfere. Keep-alive: ping/pong frames maintain the connection and the server handles unresponsive clients correctly. Close: the connection closes gracefully (both sides send close frames), reconnection logic fires within the expected time, and any buffered messages are not lost. Error: network interruption triggers reconnection, partial messages are handled correctly, and oversized messages are rejected rather than causing crashes.
Junior/Mid
How do you load test a WebSocket-based real-time system, and what metrics matter most?
Strong answer: I use k6 (which has WebSocket support) or Artillery to simulate thousands of concurrent persistent connections rather than the request-per-second model used for HTTP load testing. Key metrics: connection establishment time at scale (can the server accept 10,000 simultaneous upgrades?), message latency under load (does p99 message delivery stay under the SLO as concurrent connections increase?), connection stability (what percentage of connections drop during a 1-hour soak test?), and memory consumption per connection (WebSocket connections hold server memory — does memory grow linearly with connections, or does it leak?). For NZ financial data feeds where message ordering matters, I also test that message sequence is preserved under load.
Mid/Senior
Q1: What Playwright API do you use to listen to WebSocket frames during a test?
page.on('websocket', ws => { ... }) gives you the WebSocket object. From there, ws.on('framereceived', frame => ...) captures server-to-client messages and ws.on('framesent', frame => ...) captures client-to-server messages. Use expect.poll() to wait for specific frames to arrive asynchronously.
Q2: Name three WebSocket-specific failure modes that REST API tests would miss.
(1) Idle timeout — the server closes the connection after a period of inactivity and the client does not reconnect. (2) Silent connection drop — the connection drops without sending a close frame (close code 1006), and the client UI freezes silently. (3) Memory leak from unclosed connections — each page navigation opens a new WebSocket but the old one is never explicitly closed, leading to resource exhaustion under load.
Q3: What is a WebSocket ping/pong frame and why should you test for it?
A ping frame is sent by one side (usually the server) to check whether the connection is still alive. The other side must respond with a pong frame. This heartbeat mechanism keeps the connection open through NAT gateways and proxies that would otherwise close idle TCP connections. You should test that your client responds to pings correctly — failure to do so means the connection will be silently dropped by network infrastructure after a few minutes of low traffic.
Q4: Your team is building a real-time KiwiSaver balance dashboard for a bank. WebSocket messages arrive every 30 seconds with updated unit prices. The product owner says "just test that the REST API returns the right price — the front end handles display." What is wrong with this approach and what would you test instead?
A: The REST API only confirms the data store is correct — it says nothing about whether the WebSocket connection opens, whether price update frames actually reach the browser, or whether the connection survives 30-second idle gaps between updates (a classic idle-timeout failure scenario). You would test: (1) that the WebSocket connection is established on page load; (2) that a price update frame arrives within an acceptable window after the server publishes one; (3) that the connection survives a 30-second idle period without dropping; and (4) that the UI re-renders the new balance without a page refresh. For a bank dashboard in NZ, regulatory obligations around accurate real-time disclosure make these tests business-critical, not optional.
Q5: When would you choose Server-Sent Events (SSE) testing over WebSocket testing, and how does the testing approach differ?
A: Use SSE testing when the feature is unidirectional — the server pushes data to the client but the client never sends messages back (e.g. a live feed of TransitNZ traffic incidents). WebSocket is bidirectional and appropriate when the client also sends data (e.g. a chat message or a bid). The testing approach differs too: SSE is HTTP-based, so you can test it with standard HTTP tools by asserting on the text/event-stream response and event data. WebSocket requires Playwright's page.on('websocket', ...) listener or a dedicated WS client like wscat. Choosing the wrong mental model — treating an SSE feature as a WebSocket — means you configure the wrong tooling and miss protocol-specific failure modes.
Q6: A developer on your team says "we don't need to test WebSocket reconnection — if the connection drops, the user just refreshes the page." What is wrong with this reasoning and how do you respond?
A: This argument treats a silent failure as an acceptable user experience. Users on mobile networks in NZ, particularly outside main centres, experience frequent brief connection drops. Expecting them to notice and manually refresh is unreliable — especially on long-running dashboards like a freight tracker or an Benefits NZ case management portal. A 30-second idle timeout on an AWS load balancer will silently drop the connection; without automatic reconnection, the UI freezes with no visible error. The correct response is to assert that the client reconnects automatically within a defined SLA (typically 5–10 seconds) using page.context().setOffline(true/false) and count WebSocket open events. The feature is not complete until reconnection is tested and reliable.
Q7: In an interview, you are asked "how do you prevent WebSocket tests from being flaky?" What are the three most important things to say?
A: (1) Replace all waitForTimeout fixed sleeps with expect.poll() — fixed sleeps are the primary cause of flakiness in async tests; polling exits as soon as the condition is true and fails with a clear message on timeout. (2) Isolate each test's WebSocket listener using a fresh browser context or page — shared listeners leaking frame data from a previous test into later assertions are a common hidden flakiness source. (3) Clean up explicitly in afterEach by closing pages and contexts, so unclosed WebSocket connections from one test do not interfere with the next. Worth adding: run latency-sensitive tests (e.g. "update within 500ms") against a staging environment in the same region as your users, not localhost — NZ apps hosted offshore will behave very differently under real network conditions.
13 ISTQB Mapping
CTAL-TA v3.1.2 Section 3.2.4 — Testing web services, including WebSocket as a stateful, persistent communication protocol distinct from request-response HTTP.
CTAL-TA v3.1.2 Section 3.2.7 — Non-functional testing: performance and reliability of real-time connections, including latency thresholds and reconnection behaviour.
14 Next Steps
WebSocket is one piece of the API testing picture. Go deeper on the related techniques: