使用 Jest 测试时 componentWillUpdate 上的 nextState 不正确(也使用 react-router 包装器)

IT技术 javascript reactjs react-router jestjs reactjs-testutils
2021-05-20 07:51:35

我正在使用 Jest 0.4.0。我有一个组件(来自 react-router 文档):

var stubRouterContext = (Component, props, stubs) => {
  function RouterStub() { }

  Object.assign(RouterStub, {
    makePath () {},
    makeHref () {},
    transitionTo () {},
    replaceWith () {},
    goBack () {},
    getCurrentPath () {},
    getCurrentRoutes () {},
    getCurrentPathname () {},
    getCurrentParams () {},
    getCurrentQuery () {},
    isActive () {},
    getRouteAtDepth() {},
    setRouteComponentAtDepth() {}
  }, stubs)

  return React.createClass({
    childContextTypes: {
      router: React.PropTypes.func,
      routeDepth: React.PropTypes.number
    },

    getChildContext () {
      return {
        router: RouterStub,
        routeDepth: 0
      };
    },

    render () {
      return <Component {...props} />
    }
  });
};

我的组件使用componentWillUpdate

  getInitialState: function(){
    return {something: ""};
  },
  componentWillUpdate: function(nextProps, nextState) {
    if(nextState.something === "12345"){
      this.context.router.transitionTo("MyRoute", {id: nextState.something});
    }
  },

在我的测试中:

var ComponentWrapper = stubRouterContext(MyComponent, {});
var myComponentInstance = TestUtils.renderIntoDocument(<ComponentWrapper />);

it('expects to do something on componentWillUpdate', function(){
  myComponentInstance.setState({something: "12345"});
  expect(myComponentInstance.getChildContext().router.transitionTo.mock.calls[0][0]).toEqual('MyRoute'); 
  expect(myComponentInstance.getChildContext().router.transitionTo.mock.calls[0][1]).toEqual({id: '12345'});
});

像我打电话setState,我nextStatecomponentWillUpdate永远是something: ""但是,在测试中,如果我检查的内容myComponentInstance.statesomething: "12345". 所以基本上,componentWillUpdate被调用但没有新状态,即使我的实例组件有它。

对此有何想法?

——

编辑 1

以下建议基于 setState 是异步函数,但这并没有解决问题。我还试图以这种方式模拟商店变化(Flux 模式):

myStore.getState = jest.genMockFunction().mockImplementation(function() {
   return{
    something: "12345",
   };
});

myComponentInstance.onChange(); //Simulate store change (this function has setState inside taking values from the store)

好吧,那也没有用,实际上是在告诉我onChange未定义。所以我的问题是 react-router 包装器。我找到了一个解决方案,但我不确定是否有更好的解决方案,因为这个看起来很hacky。它是以下内容:

var RouterWrapper = stubRouterContext(Component, {ref: "myRealComponentInstance"});
var renderedComponent = TestUtils.renderIntoDocument(<RouterWrapper />);
var myComponentInstance = renderedComponent.refs.myRealComponentInstance;

这样,无论是myComponentInstance.setState模拟还是模拟myComponentInstance.onChange存储都可以工作,我不需要使用异步函数。

2个回答

setSate 是一个异步函数,因此您需要使用如下所示的回调:

myComponentInstance.setState({something: "12345"}, function() {
  expect(myComponentInstance.getChildContext().router.transitionTo.mock.calls[0][0]).toEqual('MyRoute');
  expect(myComponentInstance.getChildContext().router.transitionTo.mock.calls[0][1]).toEqual({
    id: '12345'
  });
});

setState 通常是异步的,完全不能依赖于同步。如果要检查状态是否实际更改,则需要向它传递第二个参数的函数回调。

将您的期望调用放在回调中,您应该没问题。