jest: doc : jest fake timers : expect on setTimeout not working
š Bug Report
In https://jestjs.io/fr/docs/timer-mocks, we can see that we can assert that setTimeout has been called once : expect(setTimeout).toHaveBeenCalledTimes(1);
However, if you do this in a test, jest will complain :
expect(received).toHaveBeenCalledTimes(expected)
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function setTimeout]
216 | //TODO: test refresh works
> 217 | expect(setTimeout).toHaveBeenCalledTimes(1);
| ^
218 | });
219 | });
220 |
at Object.<anonymous> (xxx.spec.ts:217:24)
I think the documentation should be fixed to explain how we can do ā¦
About this issue
- Original URL
- State: open
- Created 3 years ago
- Comments: 19 (6 by maintainers)
I confirm that I also get
ReferenceError: setTimeout is not defined
in27.0.3
, the scenario is as follows:Test A passes, but code executed by Test B fails,
console.log(setTimeout)
in that code returnsundefined
. If I remove the spy on Test A, then Test B passes.@sigveio , not testing
setTimeout
, but a callback instead as you mention in previous comments is not an option for me. MysetTimeout
performs a recursive call to the same function, which is not exposed. Something like:I went by all the reports about it not working and thought that perhaps it was sacrificed for the fact that relying on an external library greatly simplifies things for Jest.
But actually, I was partially wrong and should have tested it more thoroughly.
After you have enabled the fake timers you can spy on the global:
That said; I do still stand by my comment on it most often being more favourable not to do so.
When you use the modern fake timers, āprocessor timeā should not play into the millisecond timing of when a given task can be expected to run though, because time is entirely faked. So with for example
jest.advanceTimersByTime()
you do have a lot of power.I would also think that tasks under fake timers would run in the natural order they are scheduled in. So if you want to ignore the exact timingā¦ and only care about the orderā¦ then perhaps you can use
jest.runAllTimers()
to fast forward in time and exhaust all the queues, and thentoHaveBeenNthCalledWith()
to verify them?How is one supposed to solve this issue?
My tests start to fail as described in the inital report (i.e. I get a āreceived value must be a mock or spy functionā error when invoking
expect(setTimeout).not.toHaveBeenCalled()
in a test).Adding
jest.spyOn(window, 'setTimeout')
inexplicably produces a āReferenceError: setTimeout is not definedā error:Iām using
testEnvironment: 'jsdom'
. The functionwindow.setTimeout
does exist in the test, so I donāt really understand how it can appear as not defined to the test runner.Iām experiencing a very strange return of this issue in the same project as before.
Iāve made changes to my TypeScript source code (effectively adding 2
await
statements to function calls) and doing so causes the jest to crash when running the tests:The underlying error is once more āReferenceError: setTimeout is not definedā. The tests donāt run at all. The
test(ā¦)
blocks are completely unchanged and start off with the linejest.spyOn(global, 'setTimeout')
. Removing it stops jest from crashing butāvery much expectedlyācauses my tests to fail. This happens on Jest 27 using fake timers and JSDOM as the test environment.Reproduction steps:
npm install
npm test
to see the tests passif (await shouldStopPolling()) {
(note the addedawait
)npm test
to see the tests crashSince this issue is tagged with āneeds reproā, here is a repro.
I copied the example from the docs exactly, and setTimeout is not mocked. When I use legacy timers, the documented example works as expected. This suggests that the documentation demonstrates the legacy timers, not the modern timers. That document was last updated 8 months ago, and the commit history doesnāt seem to suggest that the document was changed since the migration to modern timers.
I canāt actually find a document on the jest site for modern timers. Hopefully this reflects my own inability to find the right search terms, rather than that jest has migrated to an undocumented timer mock API?
if you are using jest 27, it uses modern timers now by default you will need to spy on window.setTimeout beforeHands