I was not able to find any luck with socket.io-mock
. My assumption is that it might have become deprecated with some newer version of socket.io, having been last updated 3 years ago.
I found much more success following this article:
https://www.bomberbot.com/testing/testing-socket-io-client-apps-a-comprehensive-guide/
Mock Setup
They define the socket.io-client mock in a jest.setup.js
file. This is how I got it to work, however I am not 100% sure that putting the mock in the jest.setup.js is required.
jest.setup.js
:
jest.mock('socket.io-client', () => {
const emit = jest.fn();
const on = jest.fn();
const off = jest.fn();
const socket = { emit, on, off };
return {
io: jest.fn(() => socket),
}
});
jest.config.js
:
module.exports = {
...
setupFilesAfterEnv: [‘<rootDir>/jest-setup.js‘],
...
};
Test Incoming Messages
MyComponent.test.tsx
:
import { render, screen, waitFor } from ‘@testing-library/react‘;
import { io as mockIo } from ‘socket.io-client‘;
import MyComponent from ‘./MyComponent‘;
describe(‘MyComponent‘, () => {
it(‘should render incoming messages‘, async () => {
render(<MyComponent />);
// wait for io() to be called: client initialized
await waitFor(() => {
expect(mockIo).toHaveBeenCalled();
});
const socket = mockIo.mock.results[0].value;
// mock an incoming message and add it to the queue
act(() => {
socket.on.mock.calls.find(([event]) => event === ‘message‘)?.[1]({
id: ‘1‘,
text: ‘Hello world!‘,
sender: ‘Alice‘,
timestamp: Date.now(),
});
});
// test that message content is displayed
expect(screen.getByText(‘Hello world!‘)).toBeInTheDocument();
expect(screen.getByText(‘Alice‘)).toBeInTheDocument();
});
});
Test Outgoing Messages
MyComponent.test.tsx
...continued:
...
it(‘should send messages and display delivery status‘, async () => {
render(<MyComponent />);
const messageInput = screen.getByPlaceholderText(‘Enter message‘);
const sendButton = screen.getByRole(‘button‘, { name: /send/i });
userEvent.type(messageInput, ‘Hello!‘);
userEvent.click(sendButton);
expect(screen.getByText(‘Hello!‘)).toBeInTheDocument();
expect(screen.getByLabelText(‘Sending‘)).toBeInTheDocument();
// look for calls to socket.emit() and test call arguments
const socket = mockIo.mock.results[0].value;
const emitCalls = socket.emit.mock.calls;
const [topic, msg] = emitCalls[0];
expect(topic).toEqual("message");
expect(msg).toEqual(expect.objectContaining({
text: ‘Hello!‘
}));
// mock a server response message to confirm that the initial message was delivered
act(() => {
socket.on.mock.calls.find(([event]) => event === ‘message-delivered‘)?.[1]({
id: ‘2‘,
});
});
await waitFor(() => {
expect(screen.queryByLabelText(‘Sending‘)).not.toBeInTheDocument();
});
expect(screen.getByLabelText(‘Delivered‘)).toBeInTheDocument();
});
...