Rec*_*tor 6 javascript unit-testing jestjs debounce
我正在尝试为去抖功能编写单元测试。我很难考虑这一点。
这是代码:
function debouncer(func, wait, immediate) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
if (!immediate) func.apply(this, args);
}, wait);
if (immediate && !timeout) func.apply(this, args);
};
}
Run Code Online (Sandbox Code Playgroud)
我应该如何开始?
小智 24
实际上,您不需要使用Sinon 来测试去抖动。Jest 可以模拟 JavaScript 代码中的所有计时器。
查看以下代码(它是 TypeScript,但您可以轻松将其转换为 JavaScript):
import * as _ from 'lodash';
// Tell Jest to mock all timeout functions
jest.useFakeTimers();
describe('debounce', () => {
let func: jest.Mock;
let debouncedFunc: Function;
beforeEach(() => {
func = jest.fn();
debouncedFunc = _.debounce(func, 1000);
});
test('execute just once', () => {
for (let i = 0; i < 100; i++) {
debouncedFunc();
}
// Fast-forward time
jest.runAllTimers();
expect(func).toBeCalledTimes(1);
});
});
Run Code Online (Sandbox Code Playgroud)
更多信息:定时器模拟
Mis*_*s94 16
如果在您的代码中您这样做:
import debounce from 'lodash/debounce';
myFunc = debounce(myFunc, 300);
Run Code Online (Sandbox Code Playgroud)
并且您想测试该函数myFunc或调用它的函数,那么在您的测试中,您可以模拟debounceusing的实现jest以使其仅返回您的函数:
import debounce from 'lodash/debounce';
// Tell Jest to mock this import
jest.mock('lodash/debounce');
it('my test', () => {
// ...
debounce.mockImplementation(fn => fn); // Assign the import a new implementation. In this case it's to execute the function given to you
// ...
});
Run Code Online (Sandbox Code Playgroud)
来源:https : //gist.github.com/apieceofbart/d28690d52c46848c39d904ce8968bb27
Bri*_*ams 10
您可能需要检查去抖功能中的逻辑:
timeout将始终由最后一条if()语句设置thisundefined因为箭头函数使用“ this封闭词法上下文的值”,所以将始终如此,并且debouncer()被设计为用作独立函数。话虽如此,听起来您的真正问题是关于测试去抖动功能。
您可以通过使用模拟来跟踪功能调用和使用伪造的计时器来模拟时间的流逝来测试功能是否被去抖动。
这是一个使用Jest模拟功能和使用from进行反跳功能的Sinon伪计时器的简单示例:debounce()Lodash
const _ = require('lodash');
import * as sinon from 'sinon';
let clock;
beforeEach(() => {
clock = sinon.useFakeTimers();
});
afterEach(() => {
clock.restore();
});
test('debounce', () => {
const func = jest.fn();
const debouncedFunc = _.debounce(func, 1000);
// Call it immediately
debouncedFunc();
expect(func).toHaveBeenCalledTimes(0); // func not called
// Call it several times with 500ms between each call
for(let i = 0; i < 10; i++) {
clock.tick(500);
debouncedFunc();
}
expect(func).toHaveBeenCalledTimes(0); // func not called
// wait 1000ms
clock.tick(1000);
expect(func).toHaveBeenCalledTimes(1); // func called
});
Run Code Online (Sandbox Code Playgroud)
使用现代的假定时器(Jest 27 已经默认)你可以更简洁地测试它:
import debounce from "lodash.debounce";
describe("debounce", () => {
beforeEach(() => {
jest.useFakeTimers("modern");
});
afterEach(() => {
jest.useRealTimers();
});
it("should work properly", () => {
const callback = jest.fn();
const debounced = debounce(callback, 500);
debounced();
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(100);
debounced();
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(499);
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(1);
expect(callback).toBeCalledTimes(1);
});
it("should fire with lead", () => {
const callback = jest.fn();
const debounced = debounce(callback, 500, { leading: true });
expect(callback).not.toBeCalled();
debounced();
expect(callback).toBeCalledTimes(1);
jest.advanceTimersByTime(100);
debounced();
expect(callback).toBeCalledTimes(1);
jest.advanceTimersByTime(499);
expect(callback).toBeCalledTimes(1);
jest.advanceTimersByTime(1);
expect(callback).toBeCalledTimes(2);
});
});
Run Code Online (Sandbox Code Playgroud)
您可以将其实现为一个像这样去抖的状态钩子......
import debounce from "lodash.debounce";
import { Dispatch, useCallback, useState } from "react";
export function useDebouncedState<S>(
initialValue: S,
wait: number,
debounceSettings?: Parameters<typeof debounce>[2]
): [S, Dispatch<S>] {
const [state, setState] = useState<S>(initialValue);
const debouncedSetState = useCallback(
debounce(setState, wait, debounceSettings),
[wait, debounceSettings]
);
return [state, debouncedSetState];
}
Run Code Online (Sandbox Code Playgroud)
并测试为
/**
* @jest-environment jsdom
*/
import { act, render, waitFor } from '@testing-library/react';
import React from 'react';
import { useDebouncedState } from "./useDebouncedState";
describe("useDebounceState", () => {
beforeEach(() => {
jest.useFakeTimers("modern");
});
afterEach(() => {
jest.useRealTimers();
});
it("should work properly", async () => {
const callback = jest.fn();
let clickCount = 0;
function MyComponent() {
const [foo, setFoo] = useDebouncedState("bar", 500);
callback();
return <div data-testid="elem" onClick={() => { ++clickCount; setFoo("click " + clickCount); }}>{foo}</div>
}
const { getByTestId } = render(<MyComponent />)
const elem = getByTestId("elem");
expect(callback).toBeCalledTimes(1);
expect(elem.textContent).toEqual("bar");
jest.advanceTimersByTime(100);
elem.click();
expect(callback).toBeCalledTimes(1);
expect(elem.textContent).toEqual("bar");
jest.advanceTimersByTime(399);
expect(callback).toBeCalledTimes(1);
expect(elem.textContent).toEqual("bar");
act(() => jest.advanceTimersByTime(1));
await waitFor(() => {
expect(callback).toBeCalledTimes(2);
expect(elem.textContent).toEqual("click 1");
});
elem.click();
await waitFor(() => {
expect(callback).toBeCalledTimes(2);
expect(elem.textContent).toEqual("click 1");
});
act(() => jest.advanceTimersByTime(500));
await waitFor(() => {
expect(callback).toBeCalledTimes(3);
expect(elem.textContent).toEqual("click 2");
});
});
});
Run Code Online (Sandbox Code Playgroud)
源代码位于https://github.com/trajano/react-hooks-tests/tree/master/src/useDebouncedState
| 归档时间: |
|
| 查看次数: |
7658 次 |
| 最近记录: |