How to mock only one method using Sinon?

12,011

Solution 1

Yes, that is one of the most common uses cases for sinon@2, but I believe that what you are looking for is neither a mock nor a spy, but instead a stub.

See: https://sinonjs.org/releases/v2.4.1/stubs/

var stub = sinon.stub(object, "foo");
//do your assertions
stub.restore(); //back to normal now.

This will allow you to create a default function that replaces object.foo().

From your question, however, it sounds like a no-op stub function is not what you are after, and instead want to override it with a custom function of your own:

var stub = sinon.stub(object, "foo", function customFoo() { /* ... */ });
//do your assertions
stub.restore(); //back to normal now.

HTH!


EDIT:

How to stub a single function:

The following stubs object.sourceAttackTarget()

var stub = sinon.stub(object, "sourceAttackTarget", function sourceAttackTargetCustom(gameState, source, target) {
    //do assertions on gameState, source, and target
    // ...
    return customReturn;
});
//do any further assertions
stub.restore(); //back to normal now.

How to stub a function to test for particular parameters

var stub = sinon.stub(object, "sourceAttackTarget");
stub.withArgs(1, "foo", "bar").returns("correctTarget");
stub.returns("wrongTarget");
var output = object.sourceAttackTarget(gameState, source, target);
equal(output, "correctTarget", 'sourceAttackTarget invoked with the right arguments');
//do any further assertions
stub.restore(); //back to normal now.

(Update)

There is also .calledWith() and .calledWithMatch(), which I just found out about. Could also prove quite useful for these sorts of tests.

How to mock just one method for an object:

"Mocks come with built-in expectations that may fail your test. Thus, they enforce implementation details. The rule of thumb is: if you wouldn’t add an assertion for some specific call, don’t mock it. Use a stub instead. In general you should never have more than one mock (possibly with several expectations) in a single test." - source

... so for the question asked above, a mock is not the right thing to use, and a stub is the appropriate choice. That being said, one of the scenarios can be tested easily with a mock, and that is the expectation that a method has been called with a particular set of arguments.

var mock = sinon.mock(object);
mock.expects("sourceAttackTarget").withArgs(1, "foo", "bar");
object.sourceAttackTarget(gameState, source, target);
mock.verify();

Solution 2

Starting with [email protected], the

var stub = sinon.stub(object, "method", func);

has been removed. One should rather use:

stub(object, "method").callsFake(func)
stub.callsFake(fakeFunction);
Makes the stub call the provided fakeFunction when invoked.

var myObj = {};
myObj.prop = function propFn() {
    return 'foo';
};

sinon.stub(myObj, 'prop').callsFake(function fakeFn() {
    return 'bar';
});

myObj.prop(); // 'bar'

Source: sinonjs docs

Share:
12,011
Rana Ghosh
Author by

Rana Ghosh

Updated on July 22, 2022

Comments

  • Rana Ghosh
    Rana Ghosh almost 2 years

    I've got a single method in my namespace that I'd like to mock, but I'd prefer that all the others work normally. Is it possible to have sinon mock a specific method while leaving the others the same?

    My understanding is I'm not looking for a spy because I want to assert that the mock was called with specific parameters.

    Here's my code in CoffeeScript:

    root.targeting.handleUnitsAttack = (gameState) ->
      units = gameState.heroes.concat(gameState.badGuys)
      for source in units
        for target in units
          gameState = root.targeting.sourceAttackTarget(gameState, source, target)
      gameState
    

    I'd like to mock sourceAttackTarget and verify that its arguments are specific values.

  • Rana Ghosh
    Rana Ghosh almost 10 years
    I actually want to verify things at the end. I want to verify that the mock was called with specific parameters.
  • Rana Ghosh
    Rana Ghosh almost 10 years
    and in fact, I actually want to control what the mocked method returns too. I feel like I want everything possible, although my code is pretty simple. I'll edit my question to show it.
  • bguiz
    bguiz almost 10 years
    If you want to verify that the method was called with specific parameters, then a simple stub will suffice. Otherwise, use the second option, where you add the custom function, do your assertions within the custom function for the expected parameters, and then also return a the value that you wish.
  • Rana Ghosh
    Rana Ghosh almost 10 years
    Can you update your example showing me how to do that?
  • Rana Ghosh
    Rana Ghosh almost 10 years
    "If you want to verify that the method was called with specific parameters, then a simple stub will suffice." Can you show that part, too?
  • Rana Ghosh
    Rana Ghosh almost 10 years
    And I'd also appreciate it if you showed how to mock a single function because others will come back here one day with a valid reason to know how to do that and want to see the answer.
  • bguiz
    bguiz almost 10 years
    OK, just edited to add how to stub a function to test that particular parameters were used.
  • bguiz
    bguiz almost 10 years
    ... and just added a section on mocks - with a disclaimer, because mocks are not the right thing to do for what you have asked.
  • Rana Ghosh
    Rana Ghosh almost 10 years
    A key to my question is what do I pass in for object in this case? Is it root.targeting?
  • bguiz
    bguiz almost 10 years
    Yes, object is the variable which contains the function which you wish to stub. In this case, that would be root.targeting. What sinon does under the hood is var methodBeforeStubbing = object[methodName];
  • bguiz
    bguiz almost 10 years
    Updatred answer to add .calledWIth() and .calledWithMatch()
  • k0pernikus
    k0pernikus over 5 years
    The answer is out-dated. The correct way to use sinon stub has become stub(obj, 'meth').callsFake(fn)