How to Mock the ws
NPM Package in Jest (The Right Way)
Testing WebSocket-based apps can feel like debugging through a keyhole—especially when you're working with the popular ws
library in Node.js. If you’ve ever tried writing Jest tests and found yourself asking "How do I mock the ws
package in Jest?" — you're not alone.
In this article, I’ll walk you through how to properly mock the ws
npm package using Jest, ensuring your WebSocket logic stays covered and your test suite stays sane.
💡 Why Mock the ws
Package?
The ws
package is a fast, simple WebSocket implementation for Node.js. While it's awesome in production, it introduces challenges in tests:
- It tries to open real connections
- It triggers async behavior that’s hard to control
- It makes your tests flaky
Instead, you should mock the WebSocket
class and control it directly.
📦 Project Setup
Make sure you have the necessary packages installed:
npm install --save ws
npm install --save-dev jest
Let’s say you have a file like websocket-client.js
:
// websocket-client.js
const WebSocket = require('ws');
class WebSocketClient {
constructor(url) {
this.ws = new WebSocket(url);
this.ws.on('open', () => {
console.log('Connected');
});
this.ws.on('message', (msg) => {
console.log('Message:', msg);
});
}
sendMessage(message) {
this.ws.send(message);
}
}
module.exports = WebSocketClient;
🧪 How to Mock ws
with Jest
Now, in your test file websocket-client.test.js
, you'll want to mock the ws
module.
✅ Step-by-Step Mocking
// __mocks__/ws.js
const WebSocketMock = jest.fn(() => ({
on: jest.fn(),
send: jest.fn(),
close: jest.fn(),
}));
module.exports = WebSocketMock;
Jest automatically uses files from
__mocks__
when usingjest.mock('ws')
.
Now write your test
// websocket-client.test.js
jest.mock('ws'); // Automatically uses __mocks__/ws.js
const WebSocket = require('ws');
const WebSocketClient = require('./websocket-client');
describe('WebSocketClient', () => {
it('should initialize and call send', () => {
const client = new WebSocketClient('ws://localhost:8080');
expect(WebSocket).toHaveBeenCalledWith('ws://localhost:8080');
// You can inspect the instance created by the constructor mock
const mockInstance = WebSocket.mock.results[0].value;
client.sendMessage('hello');
expect(mockInstance.send).toHaveBeenCalledWith('hello');
});
});
🔍 Bonus: Simulate WebSocket Events
Need to simulate an incoming message or an open event?
Modify the mock to store the event callbacks:
// __mocks__/ws.js
const WebSocketMock = jest.fn(() => {
const handlers = {};
return {
on: jest.fn((event, cb) => {
handlers[event] = cb;
}),
send: jest.fn(),
close: jest.fn(),
__trigger: (event, ...args) => {
if (handlers[event]) {
handlers[event](...args);
}
}
};
});
module.exports = WebSocketMock;
Then in your test:
it('should handle incoming message', () => {
const client = new WebSocketClient('ws://localhost:8080');
const instance = WebSocket.mock.results[0].value;
const messageSpy = jest.spyOn(console, 'log').mockImplementation();
instance.__trigger('message', 'test-message');
expect(messageSpy).toHaveBeenCalledWith('Message:', 'test-message');
messageSpy.mockRestore();
});
🧼 Conclusion
Mocking ws
in Jest isn’t black magic—it’s just a matter of intercepting the constructor and simulating behavior. With the mock approach above, your WebSocket logic becomes:
- Testable ✅
- Predictable ✅
- Maintainable ✅
If you found this guide helpful, don’t forget to share it with your dev team or star the repo you’re working on 😉.