如何在使用酶测试时等待componentDidMount中的setState解析?

时间:2017-06-08 06:49:41

标签: jestjs enzyme

我正在尝试测试运行某些异步代码并在componentDidMount中调用setState的React组件。

这是我要测试的反应组件:

/**
*
* AsyncComponent
*
*/

import React from 'react';

class AsyncComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      loaded: false,
      component: null,
    };
  }
  componentDidMount() {
    this.props.component.then(component => {
       this.setState({
         loaded: true,
         component: component.default ? component.default : component,
       });
    });
  }
  render() {
    if (!this.state.loaded) {
      return null;
    }

    const Component = this.state.component;

    const { component, ...rest } = this.props;

    return <Component {...rest} />;
  }
}

export default AsyncComponent;

以下是测试用例。我正在使用开玩笑和酶。

import React from 'react';
import { mount } from 'enzyme';

import AsyncComponent from '../index';

const TestComponent = () => <div>Hello</div>;

describe('<AsyncComponent />', () => {
  it('Should render loaded component.', () => {
    const promise = Promise.resolve(TestComponent);
    const rendered = mount(<AsyncComponent component={promise} />);
    expect(rendered.state().loaded).toBe(true);
  });
});

测试失败,因为state.loaded仍设置为false。有没有办法在调用expect之前确保AsyncComponent已经完全加载?

如果我将期望断言包装在setTimeout中,我可以使它工作,但这似乎是一种相当hacky方式。我应该怎么做呢?

3 个答案:

答案 0 :(得分:0)

我遇到了同样的问题,我想出了一些笨拙的解决方案,我在componentDidMount中有一些函数调用,我想检查是否已调用该函数,以便代码对我有用

const loadFiltersTree = jest.fn()   
const wrapper = shallow(<FilterTree loadFiltersTree={loadFiltersTree} />)
jest.useFakeTimers()
jest.runAllTimers()
setImmediate(() => {
  expect(loadFiltersTree.mock.calls.length).toEqual(1)
})

答案 1 :(得分:0)

打破承诺链是常见的反模式。根据经验,使用promise的函数应返回生成的promise链,除非这会引起问题。这保证了当调用者函数链接一个诺言时不会出现竞争条件。原因之一是可测试性的提高。这也适用于生命周期挂钩,例如componentDidMount

componentDidMount() {
  return this.props.component.then(...)
}

异步Jest测试应链接使用中的所有诺言并返回诺言。 async..await是执行此操作的实用方法。

在酶中,浅层渲染可禁用自动componentDidMount调用并链接生命周期挂钩返回的承诺:

const wrapper = shallowMount(<AsyncComponent component={promise} />,
  { disableLifecycleMethods: true });
await wrapper.instance().componentDidMount();
expect(wrapper.state().loaded).toBe(true);

这也可以通过监视componentDidMount完成完整渲染:

jest.spyOn(AsyncComponent.prototype, 'componentDidMount');
const wrapper = mount(<AsyncComponent component={promise} />);
expect(wrapper.instance().componentDidMount).toHaveBeenCalledTimes(1);
await wrapper.instance().componentDidMount.mock.results[0].value;
expect(wrapper.state().loaded).toBe(true);

答案 2 :(得分:-1)

您需要使用async/await通知jest有关承诺,或者从测试中返回承诺,请查看docs

describe('<AsyncComponent />', () => {
  it('Should render loaded component.', async() => {
    const promise = Promise.resolve(TestComponent);
    const rendered = mount(<AsyncComponent component={promise} />);
    await promise
    expect(rendered.state().loaded).toBe(true);
  });
});