如何在Puppeteer中Mock WebSocket通信?
Great question! Testing WebSocket interactions without a real backend is totally doable—here are the methods I’ve used successfully in projects, depending on how close you want the test to mimic real behavior:
For straightforward tests where you just need to trigger specific events, creating a mock WebSocket class is simple and gives you full control. You can simulate connection opens, incoming messages, closures, and even track outgoing messages for assertions.
Here’s a basic implementation you can drop into your test setup:
class MockWebSocket { constructor(url) { this.url = url; this.onopen = null; this.onmessage = null; this.onclose = null; this.onerror = null; // Simulate a delayed connection open (matches real WebSocket behavior) setTimeout(() => { if (this.onopen) this.onopen({ type: 'open' }); }, 100); } send(data) { // Track sent messages so you can verify your app is sending the right stuff console.log('Mock WebSocket sent:', data); // Or store in an array for later assertions: // MockWebSocket.sentMessages.push(data); } // Helper to simulate server sending a message to the client triggerMessage(messageData) { if (this.onmessage) { this.onmessage({ type: 'message', data: JSON.stringify(messageData) // Adjust format to match your app's expectations }); } } // Helper to trigger a close event triggerClose(code = 1000, reason = 'Normal closure') { if (this.onclose) { this.onclose({ type: 'close', code, reason }); } } } // Replace the global WebSocket with your mock before running tests global.WebSocket = MockWebSocket;
In your test, you’d initialize your app, grab the mock WebSocket instance, and trigger events to see how your app reacts:
test('app displays notification on WebSocket message', () => { // Initialize your app (which creates a WebSocket connection) initMyApp(); // Get the mock WebSocket instance (adjust based on how your app stores it) const socket = global.WebSocket.instances[0]; // Simulate the server sending a notification socket.triggerMessage({ type: 'alert', text: 'New update available!' }); // Assert your app updated correctly (e.g., check the DOM) expect(document.querySelector('.alert-banner').textContent).toBe('New update available!'); });
If you’re using Jest, Vitest, or another framework with built-in mocking, you can spy on or mock the WebSocket API directly without writing a full class. This is great for quick, focused tests.
Example with Jest:
describe('WebSocket behavior', () => { let mockSocket; let mockOnMessageCallback; beforeEach(() => { // Create a mock socket object with the methods your app uses mockOnMessageCallback = jest.fn(); mockSocket = { onopen: jest.fn(), onmessage: mockOnMessageCallback, onclose: jest.fn(), send: jest.fn(), // Add a helper to trigger incoming messages simulateMessage: (data) => { mockOnMessageCallback({ data: JSON.stringify(data) }); } }; // Mock the global WebSocket to return our mock socket global.WebSocket = jest.fn(() => mockSocket); }); afterEach(() => { // Clean up mocks after each test jest.restoreAllMocks(); }); test('app sends a message when user clicks "Send"', () => { initMyApp(); // Simulate user action that triggers a WebSocket send document.querySelector('#send-btn').click(); // Assert the app sent the correct message expect(mockSocket.send).toHaveBeenCalledWith(JSON.stringify({ action: 'user_message', text: 'Hello server!' })); }); test('app updates state on incoming message', () => { initMyApp(); // Simulate server sending data mockSocket.simulateMessage({ type: 'chat', user: 'Bot', message: 'Hi there!' }); // Assert your app's state or UI changed (adjust based on your framework) expect(document.querySelector('.chat-message').textContent).toBe('Bot: Hi there!'); }); });
For more end-to-end-like tests where you want to simulate real back-and-forth communication, use a mock server library. These let you define how the "server" responds to incoming messages, which is perfect for testing complex flows like request-response cycles.
Here’s how you might use one (install via npm/yarn first):
import { Server } from 'mock-socket'; describe('Full WebSocket flow', () => { let mockServer; beforeEach(() => { // Create a mock server at a fake URL your app connects to mockServer = new Server('ws://localhost:9000'); // Define server behavior: respond to client messages mockServer.on('connection', (socket) => { socket.on('message', (clientData) => { const parsedData = JSON.parse(clientData); if (parsedData.action === 'get_status') { // Send a response back to the client socket.send(JSON.stringify({ status: 'online', users: 5 })); } }); }); }); afterEach(() => { // Close the mock server after each test mockServer.close(); }); test('app fetches and displays server status', async () => { initMyApp(); // App connects to ws://localhost:9000 // Wait for the connection to establish await new Promise(resolve => mockServer.on('connection', resolve)); // Trigger the app to request status document.querySelector('#check-status').click(); // Wait for the response to be processed await new Promise(resolve => setTimeout(resolve, 150)); // Assert the UI shows the correct status expect(document.querySelector('.status-text').textContent).toBe('Online (5 users)'); }); });
Pro Tips:
- Test edge cases: Don’t forget to simulate connection errors, invalid message formats, or unexpected closures to make sure your app handles them gracefully.
- Framework-specific tweaks: If you’re using React, Vue, or Angular, pair these methods with your framework’s testing tools (like React Testing Library) to assert component state changes instead of just DOM elements.
- Avoid hardcoding URLs: Make sure your app uses a configurable WebSocket URL so you can easily point it to your mock server in tests.
内容的提问来源于stack exchange,提问作者Pawel

