You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We have a cache-related test that requires mocking the setTimeout to assert the cache has been cleaned after a given time interval. However, the renderHook seems to do not work when using fake times.
react-hooks-testing-library version: 5.1.1
react version: 16.0.0
react-dom version (if applicable): 16.0.0
What happened:
Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.
Reproduction:
jest.useFakeTimers();constcache: {promise?: Promise<any>,value?: any}={};constuseExample=()=>{if(cache.value!==undefined){returncache.value;}if(!cache.promise){cache.promise=newPromise(resolve=>setTimeout(()=>resolve('foo'),10)).then(result=>{cache.value=result;});}throwcache.promise;};it('should not fail',async()=>{const{result, waitForNextUpdate}=renderHook(()=>useExample());jest.runAllTimers();awaitwaitForNextUpdate();expect(result.current).toBe('foo');});
The text was updated successfully, but these errors were encountered:
marcospassos
changed the title
renderHook fails for suspense-based hooks using useFakeTimers
renderHook fails for suspense-based hooks using jest.useFakeTimers
Apr 6, 2021
Disclaimer: I am not an expert in any of this so this is my understanding of what is going on but there may be some inaccuracies of mistakes in my mental model of it all.
For starters, this isn't an issue with @testing-library/react-hooks but rather trying to mix promises and fake timers with jest. Running jest.runAllTimers() does trigger the timeout to fire, however, the then (and other microtasks) of the promise does not run until the test awaits as it is always asynchronous.
You can work around this by:
awaiting something else that will resolve to give your promise a chance to flush its microtasks
using our rerender functionality to force the new value to be returned
I'm not actually sure why this is required as the promise resolving should be enough to trigger the render, but I suspect there is something deep in the guts of the react renderer (react-dom based on the provided info) that needs the promise to be flushed.
The simplest way I could find to do this was:
it("should not fail",async()=>{const{ result, rerender }=renderHook(()=>useExample());jest.runAllTimers();awaitPromise.resolve();rerender()expect(result.current).toBe("foo");});
Secondly, the async utils are used when the test needs to wait for async behaviour in a hook. By faking the timers, the updates are no longer asynchronous (excluding the above described weirdness of mixing timeouts and promises). An alternative approach would be to not use fake timers and allow an async util to actually wait for it to happen:
// do not call jest.useFakeTimers();it("should not fail",async()=>{const{ result, waitForNextUpdate }=renderHook(()=>useExample());awaitwaitForNextUpdate()expect(result.current).toBe("foo");});
This results in a slightly slower test, however, I feel that not faking timers allows the test to be more robust to changes in the implementation on the test.
Problem description:
We have a cache-related test that requires mocking the
setTimeout
to assert the cache has been cleaned after a given time interval. However, therenderHook
seems to do not work when using fake times.react-hooks-testing-library
version: 5.1.1react
version: 16.0.0react-dom
version (if applicable): 16.0.0What happened:
Reproduction:
The text was updated successfully, but these errors were encountered: