Jest: setTimeout not triggering when unit testing

Created on 26 Mar 2017  ·  13Comments  ·  Source: facebook/jest

  it('mock setTimeout test', () => {    
    setTimeout(() => {
      console.log('TIME IS UP');
    }, 1000);
  });

Why is there no message in the console? I also tried using done() after the console.log statement, but the results are the same.

My jest config:

  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,jsx}"
    ],
    "setupFiles": [
      "<rootDir>/config/polyfills.js"
    ],
    "testPathIgnorePatterns": [
      "<rootDir>[/\\\\](build|docs|node_modules|scripts)[/\\\\]"
    ],
    "testEnvironment": "node",
    "testURL": "http://localhost",
    "transform": {
      "^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest",
      "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
      "^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
    },
    "transformIgnorePatterns": [
      "[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"
    ],
    "moduleNameMapper": {
      "^react-native$": "react-native-web"
    }
  },
  "babel": {
    "presets": [
      "react-app"
    ]
  },
  "eslintConfig": {
    "extends": "react-app"
  }

Dependencies:
"enzyme": "^2.7.1",
"jest": "18.1.0",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react-router": "^3.0.2"

Node v 7.5.0

Another variation, propesed by stackOverflow has some interesting results:

  jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;

  it('mock setTimeout test', (done) => { // <--- add argument
    setTimeout(() => {
      console.log('TIME IS UP');
      done(new Error('time is up')); // <--- indicate the test finished with error
    }, 1000);
    // ...
    console.log('I ran after the timer');
    // ...
  });

'I ran after the timer' gets printed to the console first.
Afterwards the test times out after 10 seconds, printing out:

Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

Most helpful comment

It won't log the message, because Jest buffers all console calls so they can be displayed after test results reporters, and since setTimeout() delegates console.log() further into the function call queue, it's apparently called after we can collect it, which results in message not being logged.

To overcome it, you can either use fake timers:

it('mock setTimeout test', () => {
  jest.useFakeTimers();
  setTimeout(() => {console.log('TIME IS UP');}, 1000);
  jest.runAllTimers();
});

or use done callback:

it('mock setTimeout test', (done) => {
  setTimeout(() => {
    console.log('TIME IS UP');
    done();
  }, 1000);
});

All 13 comments

It won't log the message, because Jest buffers all console calls so they can be displayed after test results reporters, and since setTimeout() delegates console.log() further into the function call queue, it's apparently called after we can collect it, which results in message not being logged.

To overcome it, you can either use fake timers:

it('mock setTimeout test', () => {
  jest.useFakeTimers();
  setTimeout(() => {console.log('TIME IS UP');}, 1000);
  jest.runAllTimers();
});

or use done callback:

it('mock setTimeout test', (done) => {
  setTimeout(() => {
    console.log('TIME IS UP');
    done();
  }, 1000);
});

@thymikee,

Please see the second part of the issue about the done callback - it does not trigger at all. I also tried this with running expect(2).toBe(1) instead of console.log, and got the same results:

Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

Please not that I have set the jasmine default timout interval to 10s in order to compensate for the 1sec timer.

Does it happen on Jest 19?

Yes, just updated jest to "jest": "^19.0.2", basically same issue.

The test:

  it('mock setTimeout test', (done) => {
    let zero = 0;
    setTimeout(() => {
      zero = 1;
      expect(zero).toBe(1);
      done();
    }, 1000);
  });

Times out:

Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

  at Timeout.callback [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:523:19)
  at ontimeout (timers.js:365:14)
  at tryOnTimeout (timers.js:237:5)
  at Timer.listOnTimeout (timers.js:207:5)

The test:

  it('mock setTimeout test', (done) => {
    let zero = 0;
    setTimeout(() => {
      zero = 1;
      done();
    }, 1000);
    expect(zero).toBe(1);
  });

Fails (expected value to be 1, received 0).

Sorry but I cannot repro this. Can you setup a github repo with the case?
Also, what did you expect from second test? setTimeout() (and done inside) will be evaluated after the expect() call so it fails for obvious reasons.

Ugh, stupid issue:

I was calling jest.useFakeTimers(); in a previous test - this is why the mock setTimeout test timed out. After setting up a separate project and running only this test I can see that everything is working as expected.

So the initial anwser was correct - either use fake timers, or pass a done callback (both options work).

Sorry for the trouble

Cool, have fun testing! 😄

So, would you expect the below test to fail the assertion?

  it('fails if failed expectation with done - async', done => {
    setTimeout(() => {
      expect(true).toEqual(false);
      done();
    }, 1);
  });

I copied this straight from the jest tests here.

But I don't get a failed test, I get a timeout.

 FAIL  scripts/convert.test.js (5.247s)
  ✕ fails if failed expectation with done - async (5002ms)

  ● fails if failed expectation with done - async

    Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

      at node_modules/jest-jasmine2/build/queue_runner.js:64:21
      at Timeout.callback [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:523:19)
      at ontimeout (timers.js:488:11)
      at tryOnTimeout (timers.js:323:5)
      at Timer.listOnTimeout (timers.js:283:5)

This, however, does wait for a second then run the test:

it('mock setTimeout test', async () => {
  console.time('waiting for test');

  await new Promise(resolve => {
    setTimeout(() => {
      resolve();
    }, 1000);
  });

  console.timeEnd('waiting for test');

  expect(0).toBe(1);
});

Jest: 21.2.1
Node: 8.1.2

Jest config:

"jest": {
  "clearMocks": true,
  "collectCoverageFrom": [
    "src/**/*.{js,jsx}"
  ],
  "setupFiles": [
    "<rootDir>/config/jest/requestAnimationFrameStub.js",
    "<rootDir>/config/polyfills.js"
  ],
  "setupTestFrameworkScriptFile": "<rootDir>/scripts/setupTestFrameworkScriptFile.js",
  "testMatch": [
    "<rootDir>/src/**/__tests__/**/*.js?(x)",
    "<rootDir>/src/**/?(*.)(spec|test).js?(x)",
    "<rootDir>/scripts/*.(spec|test).js?(x)"
  ],
  "testEnvironment": "node",
  "testURL": "http://localhost",
  "transform": {
    "^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest",
    "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
    "^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
  },
  "transformIgnorePatterns": [
    "[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"
  ],
  "coveragePathIgnorePatterns": [
    "(.story)\\.(js)$"
  ],
  "moduleNameMapper": {
    "^react-native$": "react-native-web",
    "\\.scss$": "identity-obj-proxy"
  },
  "globals": {
    "__DEV__": true
  }
},

Thank @davidgilbertson

I tried with await new Promise(resolve => setTimeout(resolve, 1)); and it worked.

it('should trigger onChange', async () => {
    const pin = shallow(<PINInput />);

    pin.find('input').forEach((input, idx) => input.simulate('change', { target: { value: idx.toString() } }));

    await new Promise(resolve => setTimeout(resolve, 1));
    expect(pin.state('value')).toBe('0123');
});

@davidgilbertson, @ReeganExE i'm getting error
(in promise) Error: 'expect' was used when there was no current spec, this could be because an asynchronous test timed out
and my test lines look like
Component.find('.nested-list-item__children').last().childAt(8).simulate('click');
await new Promise(resolve => setTimeout(resolve, 5000));
expect(Component.instance().state.application.updatedArray.length > 0).toBe(true);

any help appreciated thanks

@kumar155 I can try an help, can you provide the whole test starting at the it?

Also, it helps if you format the code as code, see Creating and highlighting code blocks for details

@davidgilbertson here is the test

it('validate target view data', aync () => {

      // component mount
      const Component = mount(
            h(Provider, { store }, [
                h(IntlProvider, { locale: 'en' },
                    h(RDSMContainer),
                )],
           ));

     Component.find('.dropdown-choice').first().simulate('click');

     // update the component
     Component.update();

     await new Promise(resolve => setTimeout(resolve, 4000));

     const TargetComponent = RDSMComponent.find(TargetView);

     expect(TargetComponent.instance().state.applications.length > 0).toBe(true);
});

when i done simulating click which calls api in backend through integration tests, it will take 4seconds to get data and binded to my targetview here, that api call will handle my component that's not in test..
i can see the data is coming and the length is going to definately greater than 0 (i can see in my console also). but in console i see this error
(in promise) Error: 'expect' was used when there was no current spec, this could be because an asynchronous test timed out

Hi,
this test does not work

  it.only('hi', async () => {
    await new Promise(resolve => setTimeout(resolve, 1));
    expect('hello').toBe('hello');
  });
Was this page helpful?
0 / 5 - 0 ratings