可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I know how to use stub to replace one function.
sandbox.stub(Cars, "findOne",
() => {return car1 });
But now I have a line in my function I want to test that I need to stub that looks like this
Cars.find().fetch()
So there is a chain of function here and I'm unsure what I need to do. How do I stub "find" to return something that I can use to stub "fetch"?
回答1:
Try this:
sandbox.stub(Cars, "find", () => {
return {
fetch: sinon.stub().returns(anything);
};
});
回答2:
IMHO, we can just use returns
to do this. We don't need to use callsFake
or mock it as function.
// Cars.find().fetch()
sinon.stub(Cars, 'find').returns({
fetch: sinon.stub().returns(anything);
});
in case, if there is another method after fetch(), we can use returnsThis()
// Cars.find().fetch().where()
sinon.stub(Cars, 'find').returns({
fetch: sinon.stub().returnsThis(),
where: sinon.stub().returns(anything);
});
Ref:
https://sinonjs.org/releases/v6.3.3/
Hope it helps
回答3:
The form of attaching a function to a stub shown here:
sandbox.stub(Cars, "find", () => {
return {
fetch: sinon.stub().returns(anything);
};
});
is deprecated.
It's now, as of version 6.3
sandbox.stub(Cars, "find").callsFake(() => {
return {
fetch: sinon.stub().returns(anything);
};
});
回答4:
I ran into this problem and, though I liked the solution for a single test, wanted something more dynamic that would allow for reuse across tests. I also preferred the sandbox approach, as it made restoring much easier for larger suites. End result:
export function setupChainedMethodStub(sandbox: sinon.SinonSandbox, obj: any, methodName: string, methodChain: string[], value: any) {
return sandbox.stub(obj, methodName).returns(generateReturns(sandbox, methodChain, value));
}
function generateReturns(sandbox: sinon.SinonSandbox, methodChain: string[], value: any): any {
if (methodChain.length === 1) {
return {
[methodChain[0]]: sandbox.stub().returns(value),
};
} else {
return {
[methodChain[0]]: sandbox.stub().returns(generateReturns(sandbox, methodChain.slice(1), value)),
};
}
}
Wherever I want to set up a stub on the fly, I pass in the created sandbox and the other parameters:
setupChainedMethodStub(sandbox, MyMongooseModel, 'findOne', ['sort', 'exec'], { foo: 'bar' })
Then I just have a sandbox.restore()
in my highest scoped afterEach()
回答5:
This is another approach that also allows spying on chains of jQuery methods - which took me a long time to figure out.
In the example, I am trying to test that an email field is cleared out
//set up stub and spy
const valSpy = sandbox.spy();
const jQueryStub = sandbox
.stub($.prototype, "find") // this prototype is important
.withArgs("input[name=email]")
.returns({ val: valSpy });
// call function under test
learnerAlreadyAccepted(inviteDoc);
// check expectations
expect(jQueryStub).to.have.been.called; // not really necessary
expect(valSpy).to.have.been.calledWith("");
and the function under test is (roughly):
learnerAlreadyAccepted = function(doc) {
$("form").find("input[name=email]").val("");
}