使用Jest和Enzyme测试React组件中的反跳功能 [英] Testing debounced function in React component with Jest and Enzyme
问题描述
我正在使用Jest和Enzyme测试React组件,并且很难测试去抖动功能是否被正确调用(或根本没有调用)。我已经简化了下面的组件代码(已编辑,使代码更简单),链接到codepen 此处
I am testing a React component using Jest and Enzyme, and am having difficulty testing that a debounced function is called properly (or at all). I've simplified the component code below (edited to make code even simpler), link to codepen here
// uses lodash debounce
class MyApp extends React.Component {
constructor(props) {
super()
this.state = {name: "initial value"};
this.debouncedFunction = _.debounce(this.debouncedFunction, 3000);
this.handleClick = this.handleClick.bind(this)
}
debouncedFunction () {
this.setState({name: "after delay, updated value"});
}
handleClick() {
this.debouncedFunction();
}
render() {
return (
<div>
<p>{this.state.name}</p>
<button onClick={this.handleClick}>
click for debounced function
</button>
</div>
);
}
}
我认为去抖动功能测试应该非常相似到一个非去抖动但具有 setTimeout
或 Promise
的项目(预期值为
内的断言。然后或 .finally
)。在尝试了采用这两种想法的多种测试变体之后,我不再那么确定了。有任何想法吗?
I figured that the debounced function test should be pretty similar to one that is non-debounced, but with a setTimeout
or Promise
(with the expect
assertion inside .then
or .finally
). After trying many variations of tests employing both those ideas, I'm not so sure anymore. Any ideas?
推荐答案
注意:此答案也适用于 lodash.throttle
,因为它只是的包装反跳
。
NOTE: this answer also applies to lodash.throttle
since it is just a wrapper of debounce
.
Lodash的 debounce
是一个怪物,需要在测试中进行一些特殊处理,因为它不仅使用 setTimeout()
而且还:
Lodash's debounce
is a monster and needs some special treatments in test because not only does it use setTimeout()
but it also:
-
通话
setTimeout()
递归:这意味着调用jest.runAllTimers()
来模拟setTimeout
会导致无限递归错误,因为模拟了setTimeout ()
同步执行,直到它用完任务为止,在这里不是这种情况。
Calls
setTimeout()
recursively: This means callingjest.runAllTimers()
to mocksetTimeout
will lead to infinite recursion error, since mockedsetTimeout()
executes synchronously until it runs out of task, which is not the case here.
用途 Date
API :Jest v25及以下版本仅嘲笑计时器功能(例如 setTimeout
, setInterval
),而 debounce
都使用 setTimeout
和 Date
,因此我们需要模拟两者。
Uses Date
API: Jest v25 and below only mocks timer functions (e.g. setTimeout
, setInterval
) while debounce
uses both setTimeout
and Date
so we need to mock both of them.
如何解决此问题取决于您所使用的玩笑的版本。
How you fix this problem depend on what version of jest you are using.
使用另一个库模拟 Date
对象。在此示例中,我将使用advanceBy() > 玩笑日期模拟
Use another library to mock Date
object. In this example I'll use advanceBy()
from jest-date-mock
jest.useFakeTimers()
await act(async () => {
triggerDebounced()
advanceBy(DEBOUNCED_TIME + 1000) // forward Date
jest.advanceTimersByTime(DEBOUNCED_TIME) // forward setTimeout's timer
})
Jest版本26:
笑话版本26 引入了现代的伪造模式计时器同时模拟 Date
和计时器功能,它是一种选择功能,因此要使用它,您需要添加 jest.useFakeTimers('测试开始之前
Jest version 26:
Jest version 26 introduces modern mode for fake timers which mocks both Date
and timer functions, it's an opt-in feature, so in order to use it you need to add jest.useFakeTimers('modern')
before the test runs
jest.useFakeTimers("modern")
await act(async () => {
triggerDebounced()
jest.advanceTimersByTime(DEBOUNCED_TIME)
})
Jest版本27 +:
根据此 PR ,Jest v27将使用
Jest version 27+:
According to this PR, Jest v27 will use the modern implementation by default so we don't need to specify it explicitly.
jest.useFakeTimers()
await act(async () => {
triggerDebounced()
jest.advanceTimersByTime(DEBOUNCED_TIME)
})
这篇关于使用Jest和Enzyme测试React组件中的反跳功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!