NextState on componentWillUpdate is not correct when tested with Jest (using wrapper adapter as well)

I am using Jest 0.4.0. I have a component wrapped in this (from the responder 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 far as I call it setState

, mine nextState

is componentWillUpdate

always something: ""

. However, in a test, if I check the content myComponentInstance.state

then this is something: "12345"

. So basically componentWillUpdate

gets called, but doesn't have a new state, even if its instance does.

Any ideas on this?

-

EDIT 1

The suggestions below are based on the asynchronous setState function, but that didn't fix the problem. I also tried to simulate changing the store (stream pattern) like this:

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 it didn't work either, in fact I was told it was onChange

not defined. So my problem is with the reactor-router shell. I found a solution, but I'm not sure if there are better ones because it looks very hacky. This is the following:

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

      

This way, both myComponentInstance.setState

, and mimicking myComponentInstance.onChange

, poking fun at the work of the store, and I don't need to use async functions.

0


source to share


2 answers


setSate is an asynchronous function, so you need to use a callback like 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'
  });
});
      

Run codeHide result


+1


source


setState is often asynchronous and cannot rely on synchrony at all. You need to pass it a callback function for the second argument if you want to check that the state has actually changed.



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

0


source







All Articles