socketio / socketio/socket.io

Tests not work when use Timer Mocks like `jest.useFakeTimers()`

Open
#4,534 10 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

needs investigation
Dominant language
TypeScript
Stars
63.2k
Forks
10.3k
Avg merge
11d 20h
Merged PRs (30d)
2

Description

Hello, when I use jest.useFakeTimers(), I don't know how long I have to advance the timers or what method to call so that the internal code that sends and receives socket.io events will be executed

I want to know what I should do, because I want to increase the execution time of the tests, and since I have to write tests for some features that use setTimeouts, and some RxJs methods like debounce, throttle, this is essential for me.

This is a minimal example of jest with typescript.

jest.useRealTimers();

const {createServer} = require('http');
import {Server, Socket} from 'socket.io';
import {Socket as ClientSocket, io as ioc} from 'socket.io-client';

describe('my awesome project', () => {
  let io: Server;

  let serverSocket: Socket;
  let clientSocket: ClientSocket;

  beforeAll(done => {
    const httpServer = createServer();
    io = new Server(httpServer);
    httpServer.listen(() => {
      const port = httpServer.address().port;
      clientSocket = ioc(`http://localhost:${port}`);
      io.on('connection', socket => {
        serverSocket = socket;
      });
      clientSocket.on('connect', done);
    });
  });

  afterAll(() => {
    jest.useRealTimers();
    io.close();
    clientSocket.close();
  });

  test('should work', async () => {
    jest.useFakeTimers();

    const onHello = jest.fn();

    clientSocket.on('hello', onHello);

    serverSocket.emit('hello', 'world');

    jest.advanceTimersByTime(10000);
    jest.runAllTicks();
    await new Promise(jest.requireActual('timers').setImmediate);
    expect(onHello).toBeCalledTimes(1);
    // Error
    // Expected number of calls: 1
    // Received number of calls: 0
  });
});

And this is the way how I write test currently without Timer Mocks, but I need to await a setTimeout promise with N milliseconds, to make it work, the problem is this time is variable, for example if I set 4 milliseconds, sometimes the test pass and other times fail, I think is based on my pc resources and the amount of tests that are running.
But without fake timers this increment the total execution time, and I can't calculate how long to wait for when I want to wait for other methods to execute, for example from rxjs debounceTime or throttleTime

jest.useRealTimers();

const {createServer} = require('http');
import {Server, Socket} from 'socket.io';
import {Socket as ClientSocket, io as ioc} from 'socket.io-client';

describe('my awesome project', () => {
  let io: Server;

  let serverSocket: Socket;
  let clientSocket: ClientSocket;

  beforeAll(done => {
    const httpServer = createServer();
    io = new Server(httpServer);
    httpServer.listen(() => {
      const port = httpServer.address().port;
      clientSocket = ioc(`http://localhost:${port}`);
      io.on('connection', socket => {
        serverSocket = socket;
      });
      clientSocket.on('connect', done);
    });
  });

  afterAll(() => {
    io.close();
    clientSocket.close();
  });

  test('should work', async () => {
    const onHello = jest.fn();

    clientSocket.on('hello', onHello);

    serverSocket.emit('hello', 'world');

    await sleep(4);
    // sometimes pass and sometimes fail
    // with 4 milliseconds because this time is not enough time.
    //
    // So how many time should I wait?
    //
    // But without fake timers this increment total execution time,
    // and I can't calculate how long to wait for
    // when I want to wait for other methods to execute,
    // for example from rxjs debounceTime or throttleTime
    expect(onHello).toBeCalledTimes(1);
  });
});

function sleep(ms: number) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

Thank you


    "socket.io": "^4.5.3",
    "socket.io-client": "^4.5.4"
    "jest": "^29.2.2",

Platform:

  • Node: v16.13.1
  • OS: Windows 10

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the minimal Jest TypeScript example in the issue and trace the socket.io server/client event flow together with Jest fake timers. Determine what test behavior or documentation should change, and validate the result against the shown serverSocket.emit('hello', 'world') case and its expected callback count.

Written by the indexing model from the issue text.

Assessment

Tech stack
nodejs, typescript
Domain
backend-api-design, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.