使用sinon
和enzyme
我想测试以下组件:
// Apple.js
class Apple extends Component {
componentDidMount = () => {
this.props.start();
Api.get()
.then(data => {
console.log(data); // THIS IS ALWAYS CALLED
this.props.end();
});
}
render () {
return (<div></div>);
}
}
如果我只检查endApy.called
,它总是错误的。但将其包裹在setTimeout
中会使其通过。为什么总是调用console.log()
而不调用props.end
?为什么setTimeout
修复了它?有没有更好的方法呢?
// Apple.test.js
import sinon from 'sinon';
import { mount } from 'enzyme';
import Api from './Api';
import Apple from './Apple';
test('should call "end" if Api.get is successfull', t => {
t.plan(2);
sinon
.stub(Api, 'get')
.returns(Promise.resolve());
const startSpy = sinon.spy();
const endApy = sinon.spy();
mount(<Apple start={ startSpy } end={ endApy } />);
t.equal(startSpy.called, true); // ALWAYS PASSES
t.equal(endSpy.called, true); // ALWAYS FAILS
setTimeout(() => t.equal(endApy.called, true)); // ALWAYS PASSES
Api.get.restore();
});
答案 0 :(得分:10)
Api.get
是异步函数并返回一个promise,因此要在测试中模拟异步调用,您需要调用resolves
函数而不是returns
:
使存根返回一个Promise,它解析为提供的值。在构造Promise时,sinon使用Promise.resolve方法。您有责任在不提供Promise的环境中提供polyfill。
sinon
.stub(Api, 'get')
.resolves('ok');
答案 1 :(得分:2)
你的console.log(data)
总是发生,因为你的Promise确实解决了,它只是在测试结束后这样做了,这就是断言失败的原因。
通过将其包装在setTimeout中,您可以在循环中创建另一个事件,这允许您的Promise在测试完成之前解析,这意味着您的断言现在将通过。
在单元测试异步代码时,这是一个相当常见的问题。通常在setImmediate
中包含断言并从done
的回调中调用setImmediate
时解决。
答案 2 :(得分:1)
我遇到了一个类似的间谍问题,使用await Promise.all(spy.returnValues)
对我来说很好用。这样就解决了所有的承诺,之后我可以检查spy.called
。