在componentDidMount中获取数据后如何测试react组件?

IT技术 reactjs jestjs enzyme
2021-04-30 16:38:37

我有一个有条件渲染的 react 组件(如果数据被获取则渲染,否则返回 null),我想用jest & enzyme. 我遇到的问题是我想测试类中的方法之一,但.instance()一直返回 null,因此它不允许我测试实例。

我的代码看起来像这样

export default class MyComponent extends React.Component<Props, State> {
    componentDidMount() {
        this.props.fetchData.then(() => 
            this.setState({ loaded: true });
        );
    }

    methodThatIWantToTest() {
        //do some stuff here
    }

    render() {
        if (this.loaded) {
            // render stuff here
        } else {
            return null;
        }
    }
}

在测试中我想测试

describe('myComponent', () => {
   it('should do some stuff', () => {
      const shallowWrapper = shallow(<MyComponent {...props}/>);
      const method = shallowWrapper.instance().methodThatIWantToTest();
      ....such and such

   });
});

但它看起来MyComponent只返回 null 所以也shallowWrapper.instance()返回 null 。我尝试了shallowWrapper.update()很多其他的东西,但它似乎根本不想渲染..如何等待我的组件更新然后开始expect声明?

有没有人和我有类似的问题并且知道如何解决这个问题?

2个回答

它是render结果而不是一个实例nullshallowWrapper.instance()是组件类的实例,不能null用于有状态组件。正如参考文献所述:

返回 (React 16.x)

ReactComponent:有状态的 React 组件实例。

null:如果无状态 React 组件被包装。

虽然shallowWrapper.html()最初null确实如此。

原代码有错误,应该是this.state.loaded而不是this.loaded

MyComponent extends React.Component {
  state = { loaded: false };

  componentDidMount() {
    this.props.fetchData.then(() => {
          this.setState({ loaded: true });
    });
  }

  methodThatIWantToTest() {
      //do some stuff here
  }

  render() {
      if (this.state.loaded) {
          return <p>hi</p>;
      } else {
          return null;
      }
  }
}

componentDidMount并且methodThatIWantToTest应该优先考虑不同的单位。它们属于不同的测试。如果methodThatIWantToTest在生命周期钩子中被调用,它可能在componentDidMount测试中被存根

   it('should fetch data', async () => {
      const props = { fetchData: Promise.resolve('data') };
      const shallowWrapper = shallow(<MyComponent {...props}/>);
      expect(shallowWrapper.html()).toBe(null);
      await props.fetchData;
      expect(shallowWrapper.html()).toBe('<p>hi</p>');
   });

然后可以单独测试该方法。可以禁用生命周期挂钩以减少移动部件的数量:

   it('should do some stuff', () => {
      const shallowWrapper = shallow(<MyComponent {...props}/>, {disableLifecycleMethods: true});
      const result = shallowWrapper.instance().methodThatIWantToTest();
      expect(result).toBe(...);    
   });

这是一个工作示例:


我的组件.js

import * as React from 'react';

export default class MyComponent extends React.Component {

  constructor(...props) {
    super(...props);
    this.state = { loaded: false };
  }

  componentDidMount() {
    this.props.fetchData().then(() =>
      this.setState({ loaded: true })
    );
  }

  methodThatIWantToTest() {
    return 'result';
  }

  render() {
    if (this.state.loaded) {
      return <div>loaded</div>;
    } else {
      return null;
    }
  }
}

myComponent.test.js

import * as React from 'react';
import { shallow } from 'enzyme';

import MyComponent from './myComponent';

describe('myComponent', () => {
  it('should do some stuff', async () => {
    const fetchData = jest.fn(() => Promise.resolve());
    const props = { fetchData };

    const shallowWrapper = shallow(<MyComponent {...props}/>);
    expect(shallowWrapper.html()).toBe(null);

    expect(shallowWrapper.instance().methodThatIWantToTest()).toBe('result');

    // pause the test and let the event loop cycle so the callback
    // queued by then() within componentDidMount can run
    await Promise.resolve();

    expect(shallowWrapper.html()).toBe('<div>loaded</div>');
  });
});