'Typescript, React & Socket.io-client Tests
I'm trying to figure out how to write a Typescript/React app which uses socket.io to communicate to a server and thus other clients. However I'd like to write some tests in doing so.
In my sample app I have:
import io, { Socket } from 'socket.io-client';
const App = () => {
let socket: Socket;
const ENDPOINT = 'localhost:5000';
const join = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
event.preventDefault();
socket = io(ENDPOINT);
socket.emit('join', { name: 'Paola', room: '1' }, () => {});
};
return (
<div className="join-container">
<button className="join-button" onClick={join} data-testid={'join-button'}>
Sign in
</button>
</div>
);
};
export default App;
And my test looks like:
import App from './App';
import { render, screen, fireEvent } from '@testing-library/react';
import 'setimmediate';
describe('Join', () => {
let mockEmitter = jest.fn();
beforeEach(() => {
jest.mock('socket.io-client', () => {
const mockedSocket = {
emit: mockEmitter,
on: jest.fn((event: string, callback: Function) => {}),
};
return jest.fn(() => {
return mockedSocket;
}
);
});
});
afterEach(() => {
jest.clearAllMocks();
});
it('joins a chat', () => {
// Arrange
render(<App />);
const btn = screen.getByTestId('join-button');
// Act
fireEvent.click(btn);
// Assert
expect(btn).toBeInTheDocument();
expect(mockEmitter).toHaveBeenCalled();
});
});
I just want to make sure I can mock socket.io-client so that I can verify that messages are being sent to the client and that it (later) reacts to messages sent in.
However the test is failing and it doesn't seem to be using my mock.
Error: expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
Solution 1:[1]
In the manual-mocks#examples doc, there is a note:
Note: In order to mock properly, Jest needs
jest.mock('moduleName')
to be in the same scope as therequire/import
statement.
So, there are two solutions:
app.tsx
:
import React from 'react';
import io, { Socket } from 'socket.io-client';
const App = () => {
let socket: Socket;
const ENDPOINT = 'localhost:5000';
const join = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
socket = io(ENDPOINT);
socket.emit('join', { name: 'Paola', room: '1' }, () => {});
};
return (
<div className="join-container">
<button className="join-button" onClick={join} data-testid={'join-button'}>
Sign in
</button>
</div>
);
};
export default App;
Option 1: Call jest.mock
and import ./app
module in module scope of the test file.
app.test.tsx
:
import App from './App';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
let mockEmitter = jest.fn();
jest.mock('socket.io-client', () => {
return jest.fn(() => ({
emit: mockEmitter,
on: jest.fn(),
}));
});
describe('Join', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('joins a chat', () => {
// Arrange
render(<App />);
const btn = screen.getByTestId('join-button');
// Act
fireEvent.click(btn);
// Assert
expect(btn).toBeInTheDocument();
expect(mockEmitter).toHaveBeenCalled();
});
});
Option 2: Since you call the jest.mock
in beforeEach
hook, require
the './app' module in beforeEach
hook function scope as well.
app.test.tsx
:
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
describe('Join', () => {
let mockEmitter = jest.fn();
let App;
beforeEach(() => {
App = require('./app').default;
jest.mock('socket.io-client', () => {
const mockedSocket = {
emit: mockEmitter,
on: jest.fn(),
};
return jest.fn(() => mockedSocket);
});
});
afterEach(() => {
jest.clearAllMocks();
});
it('joins a chat', () => {
// Arrange
render(<App />);
const btn = screen.getByTestId('join-button');
// Act
fireEvent.click(btn);
// Assert
expect(btn).toBeInTheDocument();
expect(fakeEmitter).toHaveBeenCalled();
});
});
package version:
"jest": "^26.6.3",
"ts-jest": "^26.4.4"
jest.config.js
:
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'jsdom'
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | slideshowp2 |