问题
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