skip to Main Content

In my tests I want to replace the setTimeout method so that the tests run fast.

Originally I had the equivalent piece of code that used "any"… But of course eslint etc bleats.

I now have this piece of code which at last does not have any in it.

  let defaultTimeout: typeof globalThis.setTimeout;
  beforeEach(() => {
    defaultTimeout = global.setTimeout;
    globalThis.setTimeout = (callback: TimerHandler) => { (1)
      defaultTimeout(callback, 500);
    };
  })

However, I still get an error at globalThis.setTimeout

TS2741: Property  promisify  is missing in type  (callback:
TimerHandler) => void  but required in type  typeof setTimeout 
timers.d.ts(161, 19):  promisify  is declared here.

I know that I can resolve this by using any and unknown… is there another way?

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to the impetus from @Tal Rofe I had another look:

    I didn't realise I can just use jest to mock global functions... so this is my solution:

    jest.useFakeTimers();
    type TimeoutType = ReturnType<typeof globalThis.setTimeout>;
    const spyOn = jest.spyOn(global, 'setTimeout');
    spyOn.mockImplementation((callback: (args: void) => void): TimeoutType => {
      callback();
      return <TimeoutType>(<unknown>0); // timeout is actually a number!
    });
    

  2. Overriding functions like setTimeout, setInterval, console.* is bad practice. Why do you do it in that way? Did you consider using timer mocks?

    I assume you use Jest or Vitest, anyway the logic is identical.

    If all you want is to skip the timers, you could do

    jest.useFakeTimers();
    
    // Your test code goes here...
    
    expect(true).toEqual(true);
    
    jest.runAllTimers();
    
    jest.useRealTimers();
    

    The jest.runAllTimers(); line is the one doing the trick, "fast-forwarding" the timers, read more here: https://jestjs.io/docs/jest-object#jestrunalltimers

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search