如何等待React组件在Jest和/或Enzyme中完全更新?

时间:2017-08-22 03:00:12

标签: reactjs unit-testing asynchronous jestjs enzyme

在我的create-react-app中,我正在尝试测试一个在安装时执行多个setState的组件。

class MyComponent extends React.Component {

  state = {
    a: undefined,
    b: undefined,
    c: undefined,
  };

  fetchA() {
    // returns a promise
  }

  fetchB() {
    // returns a promise
  }

  fetchC() {
    // returns a promise
  }

  async componentDidMount() {
    const a = await fetchA();
    this.setState({ a });
  }

  async componentDidUpdate(prevProps, prevState) {
    if (prevState.a !== this.state.a) {
      const [b, c] = await Promise.all([
        this.fetchB(a);
        this.fetchC(a);
      ]);
      this.setState({ b, c });
    }
  }

  ...

}

在我的测试中,我做了类似这样的事情,尝试在setState中完成componentDidUpdate之前完成断言。

import { mount } from 'enzyme';

describe('MyComponent', () => {

  const fakeA = Promise.resolve('a');
  const fakeB = Promise.resolve('b');
  const fakeC = Promise.resolve('c');

  MyComponent.prototype.fetchA = jest.fn(() => fakeA);
  MyComponent.prototype.fetchB = jest.fn(() => fakeB);
  MyComponent.prototype.fetchC = jest.fn(() => fakeC);

  it('sets needed state', async () => {
    const wrapper = mount(<MyComponent />);
    await Promise.all([ fakeA, fakeB, fakeC ]);
    expect(wrapper.state()).toEqual({
      a: 'a',
      b: 'b',
      c: 'c',
    });
  });

});

以下是有趣的部分:上面的测试将失败,因为在进行断言时,最后一次setState调用(在componentDidUpdate中)尚未完成。那时,state.a已设置,但state.bstate.c尚未设置。

我能使它发挥作用的唯一方法是在断言之前将await Promise.resolve(null)楔入,以便给最后一个setState额外的滴答/周期完成。这看起来太黑了。

我尝试的另一件事是在setImmediate()中包装断言,只要断言通过就可以正常工作。如果失败,它将因未捕获的错误而终止整个测试。

有没有人克服过这个问题?

1 个答案:

答案 0 :(得分:4)

这就是我解决它的方式。希望有所帮助。

import { mount } from 'enzyme';

describe('MyComponent', () => {

  const fakeA = Promise.resolve('a');
  const fakeB = Promise.resolve('b');
  const fakeC = Promise.resolve('c');

  MyComponent.prototype.fetchA = jest.fn(() => fakeA);
  MyComponent.prototype.fetchB = jest.fn(() => fakeB);
  MyComponent.prototype.fetchC = jest.fn(() => fakeC);

  it('sets needed state', async (done) => {
    const wrapper = mount(<MyComponent />);
    await Promise.all([ fakeA, fakeB, fakeC ]);

    setImmediate(() => {
      // Without the try catch, failed expect will cause the
      // whole test to crash out.
      try {
        expect(wrapper.state()).toEqual({
          a: 'a',
          b: 'b',
          c: 'c',
        });
      } catch(error) {
         done.fail(error);
      }
      done();

  });

});