使用 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'
});
});
我正在使用 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'
});
});