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

核能气质少年 提交于 2020-01-03 06:00:09

问题


I am using Jest 0.4.0. I have a component wrapped into this (from react-router docs):

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

My component uses componentWillUpdate:

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

In my test:

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

As much as I call setState, my nextState in componentWillUpdate is always something: "". However, in the test, if I check the content of myComponentInstance.state then it is something: "12345". So basically, componentWillUpdate gets called but not having the new state even my instance component has it.

Any ideas on this?

--

EDIT 1

Below suggestions are based on setState being asynchronous function but that didn't solve the problem. I was also trying to simulate a store change (Flux pattern) in this way:

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

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

Well that didn't work either, actually was telling me that onChange is not defined. So my problem is with the react-router wrapper. I found a solution but I am not sure if there are better ones cause this one looks very hacky. It is the following:

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

In this way, both myComponentInstance.setState or simulating a myComponentInstance.onChange mocking the store work and I don't need to use asynchronous functions.


回答1:


setSate is an asychronous function so you need to use the callback as shown below:

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



回答2:


setState is often asynchronous and cannot be relied upon being synchronous at all. You need to pass it a function callback for the second argument if you want to check that the state actually changed.

Put your expect calls inside the callback and you should be fine.



来源:https://stackoverflow.com/questions/32462730/nextstate-on-componentwillupdate-not-correct-while-testing-with-jest-using-also

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!