Sinon: Possibility to call an arbitrary function from stubs

Created on 15 Apr 2012  ·  23Comments  ·  Source: sinonjs/sinon

I'd like a function calls(f) on stubs that allows me to execute arbitrary code when the stub is invoked. That function f could get all the arguments the initial function was called with.

I'll gladly provide a pull request for this, but I'd first like to make sure it has a chance of getting accepted.

Most helpful comment

@mantoni a spy doesn't let you define different behavior for different invocations.

I also have a use case for stub.onSecondCall().calls(function() {}). I want to perform my own assertions during those calls.

All 23 comments

This already exists:

sinon.stub(obj, "meth", function () {
    // Whatever you want in here
});

Brilliant! And it´s right there in the documentation, too! Sorry, missed it somehow.

I vote to reopen this because of the following:

// Argument matching
var stub = sinon.stub();
stub.withArgs("a").calls(f);
stub.withArgs("b").returns(something);

There already is a way to execute custom logic. I generally think non-trivial logic in tests is a bad idea, and don't want to encourage it with further features in this direction.

I wouldn't say that mocking a contract in a test is a bad idea, nor that it should be non trivial. An example could be a request handler in a router:

var mockRequest = sinon.stub();
mockRequest.withArgs(someReq, someRes).calls(function(req, res) {
  res.send('mock result 1');
});
mockRequest.withArgs(someOtherReq, someOtherRes).calls(function(req, res) {
  res.send('mock result 2');
});

This could of course be achieved with "yieldsTo", but I think that is non obvious and doesn't read very well.

However, I understand that you don't want to add to an already large api.

I also stuck with this:

var mockRequest = sinon.stub();
mockRequest.onThirdCall().calls(function() {
    console.log('Yes!');
    done();
});

Any hints on how this can be achieved is requested. :(

That would be very useful. For now, I have to make my own stub function for that :(

Why there should be an object and "method" to be able to stub(fn)? I can't understand what is the difference with spy(fn)?

+1 to @shakiba's comment. The 'answer' here, sinon.stub(obj, "meth", function () { ... }), only works when stubbing a method on an object. When creating a freestanding method stub with sinon.stub() (e.g. to pass something else as an event listener or callback), you can't use this solution.

The workaround is ugly:

var fakeObject = { fn: function () { } };
sinon.stub(fakeObject, "fn", function () { ... });
var stubIWanted = fakeObject.fn;

I'd much rather have a clean syntax like:

var stubIWanted = sinon.stub(function () { ... });

@peterflynn You can do this with a spy:

var spyYouWant = sinon.spy(function () { ... });

@mantoni a spy doesn't let you define different behavior for different invocations.

I also have a use case for stub.onSecondCall().calls(function() {}). I want to perform my own assertions during those calls.

+1 for @ChiperSoft's use case

+1 for @ChiperSoft's proposed change

@ChiperSoft You are perfectly capable of defining different behaviour for different invocations yourself. Just check the callcount in the spy.

@bion We are trying shrink the API down for 2.0. See if you can create a wrapper/plugin to extend the core functionality if you think this is useful and publish it to NPM if it works for you.

What I ended up doing was producing my own function stubbing library that let me define callbacks for each invocation. It makes the tests a lot simpler and it let me remove sinon from my projects entirely. http://npm.im/stepperbox

my stub is executing the original method why

@ppyoosuf please post usage questions to the mailing list or stack overflow, after reviewing the docs. If you find an actual issue/bug, try opening a new issue and post enough code for us to verify the issue.

his is my controller function in node.js

exports.searchDocument=function(req,res){ var type=req.query.docType; var
dep=req.query.dep; var serStr=req.query.serStr; var result; //
console.log(req.session.userMode); res.writeHead(200, {"Content-Type":
"application/json"});

    if(type===''||type===null || isNaN(type) ||dep===''||dep===null)

    // console.log("type:"+type+"dep="+dep);

    if(type==='-1' && dep==='-1')
    {
      docService.getAllDoc(serStr,function(err,data){
          if(err) throw err;
          result=data;
          res.end(JSON.stringify(result));
      });
    }
    else if(type==='-1')
        docService.getDocByDep(serStr,dep,function(err,data){
            if(err) throw err;
            result=data;
            res.end(JSON.stringify(result));
      });
    else if(dep==='-1')
      docService.getDocByType(serStr,type,function(err,data){
          if(err) throw err;
          result=data;
          res.end(JSON.stringify(result));
      });
    else
      **docService.getDocByTypeDep**(serStr,type,dep,function(err,data){
          console.log(data);
          console.log(err);
          if(err) throw err;
          result=data;
          res.end(JSON.stringify(result));
      });
                 };

here docService.getDocByTypeDep is a database service function

this.getDocByTypeDep=function(ser,typeId,depId,cb){

var myErr=null,data=null;
if(typeId==null || typeId=='' || depId==null || depId=='' ) return
cb("error",data);
var qry="";

con.query(qry,typeId,function(err,res){

if(err)
{
myErr=err;
cb(myErr,data);
}
else
{
data=res;
cb(myErr,data);
}
});

     };

i wrote the test cases by sinon stub ,but it executing the original function

it('returns the result', function(done) {
   var stub = sinon.stub(docService, 'getDocByTypeDep');
    var req = {
        query:{
            docType: '2',
            dep:'4',
            serStr:"a"
        }
    };
    // we provide the response object which the controller uses
    var res = {
        end: function(data) {
            expect(data).to.be.a("string");
            stub.restore();
            done();
        },
        writeHead:function(){

        }
    };
    // var error = new Error('Authentication failed.');
    stub.callsArgWithAsync(3,error,error);
    controllerToTest.searchDocument(req,res); // call the function

to be tested

           });

    please help me

On Thu, Jun 23, 2016 at 12:16 PM, Carl-Erik Kopseng <
[email protected]> wrote:

@ppyoosuf https://github.com/ppyoosuf please post usage questions to
the mailing list or stack overflow, after reviewing the docs. If you find
an actual issue/bug, try opening a new issue and post enough code for us to
verify the issue.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/sinonjs/sinon/issues/118#issuecomment-227964049, or mute
the thread
https://github.com/notifications/unsubscribe/APpoQazXq7BUm113wp7--RlxDaukOcMYks5qOivEgaJpZM4BSxXv
.

@ppyoosuf We are trying to keep the GitHub issues list tidy and focused on bugs and feature discussions. This is a usage question, please post it to the Sinon.JS mailinglist, so the bigger community can help answer your questions.

OK

Without this feature use of fake timers is a pain.
I have a method witch is called inside black box multiple amount of times with a timeout, to test black box I need to be able to control clock.
So I want to call clock.tick every time specific stub is hit, also I have defined behavior for this stub through withArgs and yields.

I would like to see something like:

var clock = sinong.useFakeTimers();
var stubbedMethod = sinon.stub();
stubbedMethod.onHit(()=>clock.tick(1000)).withArgs('page1').yields(null, [12, 45, 69]);

It's achievable only through by specific construction of stub, which is not always convenient or possible:

sinon.stub(object, 'method', function(callback){
  clock.tick(1000);
  callback();
})

Ability to change state of a test/environment on call is important for a lot of different tests and crucial to have in mock library.

in sinon 2.1, callsFake() will do exactly this

I didn't see this solution posted above, so adding incase it helps others:

Using the spy interface to get the args of the nth call

var someFunctionSpy = sinon.spy(someFunction)
someFunction()
var nthCallArgs = someFunctionSpy.getCall(n)
Was this page helpful?
0 / 5 - 0 ratings