Overview
I encountered different behaviors of jest.useFakeTimers
when it is called inside beforeEach
versus called outside.
Reproducible Examples
// Foo.tsx
import * as React from 'react';
import {View} from 'react-native';
const Foo: React.FC = () => {
const [timePassed, setTimePassed] = React.useState<number>(0);
const [flag, setFlag] = React.useState(true);
console.log(`timePassed: ${timePassed}, flag: ${flag}`);
React.useEffect(() => {
if (flag) {
setTimeout(() => {
if (timePassed > 1) {
setFlag(false);
}
setTimePassed(timePassed + 1);
}, 1);
}
}, [flag, timePassed]);
return <View />;
};
export default Foo;
// foo.outside.test.tsx
import * as React from 'react';
import {render} from '@testing-library/react-native';
import Foo from '../Foo';
jest.useFakeTimers();
describe('Test Foo', () => {
test('1', () => {
render(<Foo />);
jest.runAllTimers();
});
test('2', () => {
render(<Foo />);
jest.runAllTimers();
});
});
// foo.inside.test.tsx
import * as React from 'react';
import {render} from '@testing-library/react-native';
import Foo from '../Foo';
describe('Test Foo', () => {
// fake timer usage follows: https://testing-library.com/docs/using-fake-timers/
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.runOnlyPendingTimers();
jest.useRealTimers();
});
test('1', () => {
render(<Foo />);
jest.runAllTimers();
});
test('2', () => {
render(<Foo />);
jest.runAllTimers();
});
});
Test Results
outside
$ npx jest __tests__/foo.outside.test.tsx
console.log
timePassed: 0, flag: true
at log (Foo.tsx:8:11)
console.log
timePassed: 1, flag: true
at log (Foo.tsx:8:11)
console.log
timePassed: 2, flag: true
at log (Foo.tsx:8:11)
console.log
timePassed: 2, flag: false
at log (Foo.tsx:8:11)
console.log
timePassed: 3, flag: false
at log (Foo.tsx:8:11)
console.log
timePassed: 0, flag: true
at log (Foo.tsx:8:11)
console.log
timePassed: 1, flag: true
at log (Foo.tsx:8:11)
console.log
timePassed: 2, flag: true
at log (Foo.tsx:8:11)
console.log
timePassed: 2, flag: false
at log (Foo.tsx:8:11)
console.log
timePassed: 3, flag: false
at log (Foo.tsx:8:11)
PASS __tests__/foo.outside.test.tsx
Test Foo
✓ 1 (58 ms)
✓ 2 (21 ms)
inside
$ npx jest __tests__/foo.inside.test.tsx 1 ↵
console.log
timePassed: 0, flag: true
at log (Foo.tsx:8:11)
console.log
timePassed: 1, flag: true
at log (Foo.tsx:8:11)
console.log
timePassed: 2, flag: true
at log (Foo.tsx:8:11)
console.log
timePassed: 2, flag: false
at log (Foo.tsx:8:11)
console.log
timePassed: 3, flag: false
at log (Foo.tsx:8:11)
console.log
timePassed: 0, flag: true
at log (Foo.tsx:8:11)
console.log
timePassed: 1, flag: true
at log (Foo.tsx:8:11)
PASS __tests__/foo.inside.test.tsx
Test Foo
✓ 1 (62 ms)
✓ 2 (14 ms)
Observations
It seems that in the outside setup, jest.runAllTimers
runs the timer to the end in both test cases. However, in the inside setup, only the timer in the first test case runs to the end; the timer in the second test case exists prematurely.
Question
Why is the behavior of the fake timer different when jest.useFakeTimers
is called inside versus outside beforeEach
? I expect both setup to run the timer to the end. Thus, the current behavior of the inside setup is a negative surprise.
Machine and Software Info
- macOS Big Sur v11.6.5
- react: 17.0.2
- react-native: 0.65.1
- jest: 28.1.2
- @testing-library/react-native: 10.1.1
- typescript: 4.5.2