javascript - 在 Promise 中调用 setState 时 React Jest 测试失败

标签 javascript reactjs unit-testing jestjs enzyme

我正在尝试模拟一个返回 promise 的服务,以便我可以验证它是否使用正确的参数调用。调用服务的方式因 state 而异,第一次调用服务会设置 state

在 promise 中设置状态时,它不会更新,除非我将断言包装在 setTimeout 中或完全取消 promise。有没有一种方法可以通过简单的 promise 和期望来做到这一点?

我的组件:

class App extends Component {

  constructor(props) {
    super(props);
    this.state = {results: []};
    this.service = props.service;
    this.load = this.load.bind(this);
  }

  load() {
    if (this.state.results.length === 0) {
      this.service.load('state is empty')
                  .then(result => this.setState({results: result.data}));
    } else {
      this.service.load('state is nonempty')
                  .then(result => this.setState({results: result.data}));
    }
  }

  render() {
    return (
      <div className="App">
        <button id="submit" onClick={this.load}/>
      </div>
    );
  }
}

我的测试:

it('Calls service differently based on results', () => {

  const mockLoad = jest.fn((text) => {
    return new Promise((resolve, reject) => {
      resolve({data: [1, 2]});
    });
  });

  const serviceStub = {load: mockLoad};

  let component = mount(<App service={serviceStub}/>);
  let button = component.find("#submit");

  button.simulate('click');

  expect(mockLoad).toBeCalledWith('state is empty');

  button.simulate('click');

  //this assertion fails as the state has not updated and is still 'state is empty'
  expect(mockLoad).toBeCalledWith('state is nonempty');
});

如前所述,以下方法有效,但如果有办法解决,我宁愿不包装 expect:

setTimeout(() => {
    expect(mockLoad).toBeCalledWith('state is nonempty');
    done();
  }, 50);

我还可以更改我模拟函数的方式,以消除将起作用的 promise :

const mockLoad = jest.fn((text) => {
  return {
    then: function (callback) {
      return callback({
        data : [1, 2]
      })
    }
  }
});

但我只想返回一个 promise 。

最佳答案

出于性能原因,React 批量调用 setState,所以此时

expect(mockLoad).toBeCalledWith('state is nonempty');

条件

if (this.state.results.length === 0) {

很可能仍然是 true,因为 data 尚未 添加到 state


你最好的选择是

  • 要么使用forceUpdate在第一次和第二次点击事件之间。

  • 或者将测试分成两个单独的部分,同时在测试之外提取通用逻辑。甚至 it 子句也将变得更具描述性,例如:it('当状态为空时正确调用服务') 用于第一个测试,第二个测试类似。

我赞成第二种方法。

setState() does not always immediately update the component. It may batch or defer the update until later.

阅读更多 here .

关于javascript - 在 Promise 中调用 setState 时 React Jest 测试失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43689765/

相关文章:

javascript - 如何在特定日期和时间创建 `Temporal.Instant` 的实例?

javascript - index.js :1 Material-UI: The key `selectLabel` provided to the classes prop is not implemented in ForwardRef(TablePagination)

java - 用于单元测试的 Mockito 风格的 anyXXX 方法

unit-testing - Erlang rebar,内核选项

javascript - 来自 json 对象的 Highcharts 堆叠列

javascript - *ngFor 需要为每个不同的对象生成 1 个 btn,但将相似的对象值放在同一个 btn 中

reactjs - TypeScript react 错误 : "Module not found: Can' t resolve 'styled-components' "

unit-testing - Angular 2.0.0 - 模拟文件 uploader

javascript - JS 不知道大小的情况下寻址数组索引

javascript - 如何在 React 应用程序运行时加载 JSON 配置?