When to use waitForNextUpdate rather than act + jest.advanceTimersByTime?
Asked Answered
P

1

8

There is an example on advanced-hooks#async doc.

I am confused about how does waitForNextUpdate works. I made two test cases to compare waitForNextUpdate and act() + jest.advanceTimersByTime().

index.ts:

import { useState, useCallback } from 'react';

export function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);
  const increment = () => setCount((x) => x + 1);
  const incrementAsync = useCallback(() => setTimeout(increment, 100, [increment]);
  const reset = useCallback(() => setCount(initialValue), [initialValue]);
  return { count, increment, incrementAsync, reset };
}

index.test.ts:

import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './';

jest.setTimeout(5 * 1000);

describe('waitForNextUpdate  V.S. jest-advancedTimersByTime', () => {
  test('should pass by using jest.advancedTimersByTime', () => {
    jest.useFakeTimers();
    const { result } = renderHook(() => useCounter());
    result.current.incrementAsync();
    act(() => {
      jest.advanceTimersByTime(100);
    });
    expect(result.current.count).toBe(1);
    jest.useRealTimers();
  });

  test('should pass by using waitForNextUpdate', async () => {
    const { result, waitForNextUpdate } = renderHook(() => useCounter());
    result.current.incrementAsync();
    await waitForNextUpdate();
    expect(result.current.count).toBe(1);
  });
});

Test result:

 PASS  issues/waitForNextUpdate-vs-jest-advancedTimersByTime/index.test.ts
  waitForNextUpdate  V.S. jest-advancedTimersByTime
    ✓ should pass by using jest.advancedTimersByTime (13 ms)
    ✓ should pass by using waitForNextUpdate (107 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.373 s, estimated 14 s

Both test cases pass. The only difference I found is that if I increase the delay for setTimeout to 1000 * 10, the test case which uses waitForNextUpdate will fail.

// useCounter
//...
const incrementAsync = useCallback(() => setTimeout(increment, 1000 * 10), [increment]);
//...
// first test case
// ...
jest.advanceTimersByTime(1000 * 10);
// ...
 FAIL  issues/waitForNextUpdate-vs-jest-advancedTimersByTime/index.test.ts (10.554 s)
  waitForNextUpdate  V.S. jest-advancedTimersByTime
    ✓ should pass by using jest.advancedTimersByTime (19 ms)
    ✕ should pass by using waitForNextUpdate (1006 ms)

  ● waitForNextUpdate  V.S. jest-advancedTimersByTime › should pass by using waitForNextUpdate

    Timed out in waitForNextUpdate after 1000ms.

      at waitForNextUpdate (node_modules/@testing-library/react-hooks/lib/core/asyncUtils.js:102:13)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        11.112 s

So what's the difference between waitForNextUpdate and act() + jest.advanceTimersByTime()? What kind of scene I can only use waitForNextupdate rather than act() + jest.advanceTimersByTime()?

package versions:

"@testing-library/react-hooks": "^5.0.0",
"jest": "^26.6.3",
"react": "^16.14.0"
Peripheral answered 15/11, 2021 at 6:5 Comment(2)
incrementAsync is missing a closing bracket on setTimeout.Tourist
I got the answer i was looking for from your question +1Omalley
A
0

waitForNextUpdate is not related to time, but state of your hook

Wrapping incrementAsync in act() is not necessary since the state updates happen asynchronously during await waitForNextUpdate(). The async utilities automatically wrap the waiting code in the asynchronous act() wrapper.

Jest's advanceTimersByTime taps into the actual setTimeout or setInterval instances:

When this API is called, all timers are advanced by msToRun milliseconds. All pending "macro-tasks" that have been queued via setTimeout() or setInterval(), and would be executed during this time frame, will be executed.

Archicarp answered 14/9, 2022 at 21:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.