使用 Jest 和 Enzyme 测试 React 组件中的去抖动功能

IT技术 javascript reactjs jestjs enzyme
2021-05-07 13:16:32

我正在使用 Jest 和 Enzyme 测试 React 组件,并且难以测试去抖动函数是否被正确调用(或根本调用)。我已经简化了下面的组件代码(经过编辑以使代码更简单),此处链接到 codepen

// 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>
    );
  }
}

我认为去抖动函数测试应该与非去抖动函数测试非常相似,但带有一个setTimeoutor Promiseexpect.thenor 中带有断言.finally)。在尝试了采用这两种想法的多种测试变体之后,我不再那么确定了。有任何想法吗?

1个回答

注意:这个答案也适用于, lodash.throttle因为它只是debounce.

Lodashdebounce是一个怪物,需要在测试中进行一些特殊处理,因为它不仅使用,setTimeout()而且还:

  • 调用 递归:这意味着调用来模拟将导致无穷递归的错误,因为嘲笑同步执行,直到它运行的任务了,这是不是这里的情况。setTimeout() jest.runAllTimers()setTimeoutsetTimeout()

  • 使用 API:Jest v25 及以下仅模拟计时器函数(例如, ),同时使用两者因此我们需要模拟它们。Date setTimeoutsetIntervaldebouncesetTimeoutDate

您如何解决此问题取决于您使用的是哪个版本的笑话。

对于 Jest 25 及以下版本:

使用另一个库来模拟Date对象。在这个例子中,我将使用advanceBy()fromjest-date-mock

jest.useFakeTimers()

await act(async () => {
  triggerDebounced()
  advanceBy(DEBOUNCED_TIME + 1000) // forward Date
  jest.advanceTimersByTime(DEBOUNCED_TIME) // forward setTimeout's timer
})

玩笑版本 26:

Jest版本 26为假定时器引入了现代模式Date,它模拟和定时器功能,这是一个选择加入的功能,所以为了使用它,你需要jest.useFakeTimers('modern')在测试运行之前添加

jest.useFakeTimers("modern")

await act(async () => {
  triggerDebounced()
  jest.advanceTimersByTime(DEBOUNCED_TIME)
})

玩笑版本 27+:

根据这个PR,Jest v27 将默认使用现代实现,所以我们不需要明确指定它。

jest.useFakeTimers()

await act(async () => {
  triggerDebounced()
  jest.advanceTimersByTime(DEBOUNCED_TIME)
})