我正在使用Jest和Enzyme测试React组件,并且很难测试去抖动功能是否被正确调用(或根本没有调用)。我简化了下面的组件代码(已编辑,使代码更简单),链接到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
({{1}内有expect
断言}或.then
)。在尝试了使用这两种想法的多种测试变体之后,我不再那么确定了。有什么想法吗?
答案 0 :(得分:6)
注意:此答案也适用于lodash.throttle
,因为它只是debounce
的包装。
Lodash的debounce
是一个怪物,在测试中需要进行一些特殊处理,因为它不仅使用setTimeout()
,而且还使用了
Calls setTimeout()
recursively:这意味着调用jest.runAllTimers()
模拟setTimeout
将导致无限递归错误,因为模拟{{1} }同步执行,直到任务用完为止,在这里不是这种情况。
Uses Date
API:Jest v25及以下版本仅在setTimeout()
时模拟计时器函数(例如setTimeout
,setInterval
)同时使用debounce
和setTimeout
,因此我们需要模拟两者。
如何解决此问题取决于您所使用的玩笑的版本。
使用另一个库来模拟Date
对象。在此示例中,我将使用jest-date-mock
中的Date
advanceBy()
笑话version 26引入了用于假计时器的现代模式,该模式可以模拟jest.useFakeTimers()
await act(async () => {
triggerDebounced()
advanceBy(DEBOUNCED_TIME + 1000) // forward Date
jest.advanceTimersByTime(DEBOUNCED_TIME) // forward setTimeout's timer
})
和计时器功能,这是一项可选功能,因此要使用它,您需要在添加Date
之前测试运行
jest.useFakeTimers('modern')
根据此PR,Jest v27将默认使用现代实现,因此我们无需明确指定它。
jest.useFakeTimers("modern")
await act(async () => {
triggerDebounced()
jest.advanceTimersByTime(DEBOUNCED_TIME)
})