How do I block a chain of methods in Sinon?
I know how to use a stub to replace one function.
sandbox.stub(Cars, "findOne", () => {return car1 }); But now I have a string in my function. I want to check that I need to drown out what looks like this.
Cars.find().fetch() So there is a chain of functions here, and I'm not sure what I need to do. How do I drown “find” to return something that I can use to drown out “fetch”?
IMHO, we can just use returns for this. We do not need to use callsFake or mock it as a function.
// Cars.find().fetch() sinon.stub(Cars, 'find').returns({ fetch: sinon.stub().returns(anything); }); in case 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 this helps
The form for attaching the function to the stub is shown here:
sandbox.stub(Cars, "find", () => { return { fetch: sinon.stub().returns(anything); }; }); outdated.
Now, starting with version 6.3
sandbox.stub(Cars, "find").callsFake(() => { return { fetch: sinon.stub().returns(anything); }; }); I ran into this problem and although I liked the solution for one test, I wanted something more dynamic that would allow me to reuse it in different tests. I also preferred the sandbox approach, as it greatly facilitated recovery for large sets. Final 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 configure the stub on the fly, I pass other parameters to the created sandbox:
setupChainedMethodStub(sandbox, MyMongooseModel, 'findOne', ['sort', 'exec'], { foo: 'bar' })
Then I just have sandbox.restore() in my highest afterEach() scope
This is another approach that also allows you to spy on jQuery method chains, which made me think a lot.
In this example, I am trying to verify that the email field is cleared
//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 tested function (approximately):
learnerAlreadyAccepted = function(doc) { $("form").find("input[name=email]").val(""); }