我正在尝试模拟一个返回promises的服务,以便我可以验证它是否使用正确的参数调用。调用服务的方式因state
而异,第一次调用该服务会设置state
。
在promise中设置状态时,它不会更新,除非我将断言包装在setTimeout
中或完全删除了promise。有没有办法用一个简单的承诺和期望来做到这一点?
我的组件:
class App extends Component {
constructor(props) {
super(props);
this.state = {results: []};
this.service = props.service;
this.load = this.load.bind(this);
}
load() {
if (this.state.results.length === 0) {
this.service.load('state is empty')
.then(result => this.setState({results: result.data}));
} else {
this.service.load('state is nonempty')
.then(result => this.setState({results: result.data}));
}
}
render() {
return (
<div className="App">
<button id="submit" onClick={this.load}/>
</div>
);
}
}
我的测试:
it('Calls service differently based on results', () => {
const mockLoad = jest.fn((text) => {
return new Promise((resolve, reject) => {
resolve({data: [1, 2]});
});
});
const serviceStub = {load: mockLoad};
let component = mount(<App service={serviceStub}/>);
let button = component.find("#submit");
button.simulate('click');
expect(mockLoad).toBeCalledWith('state is empty');
button.simulate('click');
//this assertion fails as the state has not updated and is still 'state is empty'
expect(mockLoad).toBeCalledWith('state is nonempty');
});
如上所述,以下是有效的,但如果有解决方法,我宁愿不包括期望:
setTimeout(() => {
expect(mockLoad).toBeCalledWith('state is nonempty');
done();
}, 50);
我还可以改变我如何模拟函数来删除可行的承诺:
const mockLoad = jest.fn((text) => {
return {
then: function (callback) {
return callback({
data : [1, 2]
})
}
}
});
但我想回复一个承诺。
答案 0 :(得分:2)
出于性能原因,请对批次setState
进行批处理,因此此时
expect(mockLoad).toBeCalledWith('state is nonempty');
条件
if (this.state.results.length === 0) {
很可能仍为true
,因为data
尚未 添加到state
。
你最好的选择是
在第一个和第二个click event
之间使用forceUpdate。
或者将测试分成两个单独的,同时提取测试之外的常用逻辑。即使it
子句也会更具描述性,例如:第一次测试时为it('calls service correctly when state is empty')
,第二次测试时类似。
我赞成第二种方法。
setState()并不总是立即更新组件。它可以批量推迟更新或推迟更新。
了解更多here。
答案 1 :(得分:0)
将Sinon与Sinon Stub Promise一起使用我能够让它工作。存根承诺库删除了承诺的异步方面,这意味着state
及时更新渲染:
const sinon = require('sinon');
const sinonStubPromise = require('sinon-stub-promise');
sinonStubPromise(sinon);
it('Calls service differently based on results', () => {
const mockLoad = jest.fn((text) => {
return sinon.stub().returnsPromise().resolves({data: [1, 2]})();
});
const serviceStub = {load: mockLoad};
let component = mount(<App service={serviceStub}/>);
let button = component.find("#submit");
button.simulate('click');
expect(mockLoad).toBeCalledWith('state is empty');
button.simulate('click');
expect(mockLoad).toBeCalledWith('state is nonempty');
});
请参阅: