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

nextState on componentWillUpdate not correct while testing with Jest (using also react-router wrapper)

我正在使用 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,但我在 componentWillUpdate 中的 nextState 总是 something: ""。但是,在测试中,如果我检查myComponentInstance.state的内容,那么它是something: "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 包装器。我找到了一个解决方案,但我不确定是否有更好的解决方案,因为这个看起来很老套。如下:

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

这样,myComponentInstance.setState 或模拟一个 myComponentInstance.onChange 模拟商店工作,我不需要使用异步函数。

setState 通常是异步的,根本不能依赖于同步。如果你想检查状态是否实际改变,你需要为第二个参数传递一个回调函数。

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

setState是一个异步函数是否需要使用回调如下:

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'
  });
});