用玩笑测试自定义 UseInterval 挂钩
Testing a custom UseInterval hook with jest
我的钩子是;
function useInterval() {
const ref: MutableRefObject<NodeJS.Timer | null > = useRef(null);
function set(callback: () => void, delay: number) {
ref.current = setInterval(callback, delay)
}
function clear() {
if (ref.current) {
clearInterval(ref.current)
ref.current = null
}
}
return { set, clear }
}
我的测试是;
it("set: This should be called 10 times", () => {
var callback = jest.fn();
jest.useFakeTimers()
const { result } = renderHook(() => hooks.useInterval())
act(() => {
result.current.set(() => { callback }, 100)
jest.advanceTimersByTime(1000);
})
expect(callback).toHaveBeenCalledTimes(10);
jest.useRealTimers()
})
renderHook()
和 act()
来自 "@testing-library/react-hooks": "^7.0.2"
我不断得到的结果是 0
来自我的 expect()
调用。我似乎无法弄清楚为什么。
如果我只是使用 setInterval()
expect()
得到正确的值
it("setInterval", () => {
var callback = jest.fn();
jest.useFakeTimers()
setInterval(callback, 100)
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledTimes(10);
jest.useRealTimers()
})
我已经尝试以我能想到的所有可能的逻辑方式重新排列这些行。
我注意到无论是否使用 act()
,我都会得到相同的结果,很奇怪。
将 timers: "fake"
或其任何变体 (modern/legacy) 添加到 jest.config.ts
似乎没有任何效果。
显然,testing-library/react-hooks
以某种方式从 jest.useFakeTimers()
中屏蔽了 setInterval()
,但我不明白如何实现,因此无法实现我正在寻找的结果。
我的一部分认为我的钩子没有被 jest.useFakeTimers()
击中,因为假计时器没有被全局替换,但我不知道该怎么做。
此外,我正在使用 Typescript。并不是说我认为这会有所作为。
您将匿名函数传递给 set
方法而不是模拟 callback
方法。因此 setInterval
排队的宏任务将调用匿名函数。这就是断言失败的原因。没有什么可以开玩笑的 Config,TypeScript。
例如
useInterval.ts
:
import { MutableRefObject, useRef } from 'react';
export function useInterval() {
const ref: MutableRefObject<ReturnType<typeof setInterval> | null> = useRef(null);
function set(callback: () => void, delay: number) {
ref.current = setInterval(callback, delay);
}
function clear() {
if (ref.current) {
clearInterval(ref.current);
ref.current = null;
}
}
return { set, clear };
}
useInterval.test.ts
:
import { renderHook } from '@testing-library/react-hooks';
import { useInterval } from './useInterval';
describe('70276930', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
test('should call callback interval', () => {
const callback = jest.fn();
const { result } = renderHook(useInterval);
result.current.set(callback, 100);
jest.advanceTimersByTime(1000);
expect(callback).toBeCalledTimes(10);
});
test('should clear interval', () => {
const callback = jest.fn();
const { result } = renderHook(useInterval);
result.current.set(callback, 100);
jest.advanceTimersByTime(100);
expect(callback).toBeCalledTimes(1);
result.current.clear();
jest.advanceTimersByTime(100);
expect(callback).toBeCalledTimes(1);
});
});
测试结果:
PASS examples/70276930/useInterval.test.ts (7.541 s)
70276930
✓ should call callback interval (16 ms)
✓ should clear interval (1 ms)
----------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------------|---------|----------|---------|---------|-------------------
All files | 100 | 50 | 100 | 100 |
useInterval.ts | 100 | 50 | 100 | 100 | 9
----------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 8.294 s, estimated 9 s
包版本:
"react": "^16.14.0",
"@testing-library/react": "^11.2.2",
"jest": "^26.6.3",
我的钩子是;
function useInterval() {
const ref: MutableRefObject<NodeJS.Timer | null > = useRef(null);
function set(callback: () => void, delay: number) {
ref.current = setInterval(callback, delay)
}
function clear() {
if (ref.current) {
clearInterval(ref.current)
ref.current = null
}
}
return { set, clear }
}
我的测试是;
it("set: This should be called 10 times", () => {
var callback = jest.fn();
jest.useFakeTimers()
const { result } = renderHook(() => hooks.useInterval())
act(() => {
result.current.set(() => { callback }, 100)
jest.advanceTimersByTime(1000);
})
expect(callback).toHaveBeenCalledTimes(10);
jest.useRealTimers()
})
renderHook()
和 act()
来自 "@testing-library/react-hooks": "^7.0.2"
我不断得到的结果是 0
来自我的 expect()
调用。我似乎无法弄清楚为什么。
如果我只是使用 setInterval()
expect()
得到正确的值
it("setInterval", () => {
var callback = jest.fn();
jest.useFakeTimers()
setInterval(callback, 100)
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledTimes(10);
jest.useRealTimers()
})
我已经尝试以我能想到的所有可能的逻辑方式重新排列这些行。
我注意到无论是否使用 act()
,我都会得到相同的结果,很奇怪。
将 timers: "fake"
或其任何变体 (modern/legacy) 添加到 jest.config.ts
似乎没有任何效果。
显然,testing-library/react-hooks
以某种方式从 jest.useFakeTimers()
中屏蔽了 setInterval()
,但我不明白如何实现,因此无法实现我正在寻找的结果。
我的一部分认为我的钩子没有被 jest.useFakeTimers()
击中,因为假计时器没有被全局替换,但我不知道该怎么做。
此外,我正在使用 Typescript。并不是说我认为这会有所作为。
您将匿名函数传递给 set
方法而不是模拟 callback
方法。因此 setInterval
排队的宏任务将调用匿名函数。这就是断言失败的原因。没有什么可以开玩笑的 Config,TypeScript。
例如
useInterval.ts
:
import { MutableRefObject, useRef } from 'react';
export function useInterval() {
const ref: MutableRefObject<ReturnType<typeof setInterval> | null> = useRef(null);
function set(callback: () => void, delay: number) {
ref.current = setInterval(callback, delay);
}
function clear() {
if (ref.current) {
clearInterval(ref.current);
ref.current = null;
}
}
return { set, clear };
}
useInterval.test.ts
:
import { renderHook } from '@testing-library/react-hooks';
import { useInterval } from './useInterval';
describe('70276930', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
test('should call callback interval', () => {
const callback = jest.fn();
const { result } = renderHook(useInterval);
result.current.set(callback, 100);
jest.advanceTimersByTime(1000);
expect(callback).toBeCalledTimes(10);
});
test('should clear interval', () => {
const callback = jest.fn();
const { result } = renderHook(useInterval);
result.current.set(callback, 100);
jest.advanceTimersByTime(100);
expect(callback).toBeCalledTimes(1);
result.current.clear();
jest.advanceTimersByTime(100);
expect(callback).toBeCalledTimes(1);
});
});
测试结果:
PASS examples/70276930/useInterval.test.ts (7.541 s)
70276930
✓ should call callback interval (16 ms)
✓ should clear interval (1 ms)
----------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------------|---------|----------|---------|---------|-------------------
All files | 100 | 50 | 100 | 100 |
useInterval.ts | 100 | 50 | 100 | 100 | 9
----------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 8.294 s, estimated 9 s
包版本:
"react": "^16.14.0",
"@testing-library/react": "^11.2.2",
"jest": "^26.6.3",