Sinon: Stubbing a property getter works, but stub.called remains false

Created on 28 Aug 2017  ·  9Comments  ·  Source: sinonjs/sinon

Stubbing a property getter works, but stub.called remains false.

  • Sinon version : 3.2.1
  • Environment : Node 7
  • Other libraries you are using: None

What did you expect to happen?
Getting a stubbed property should set stub.called to true.

What actually happens
stub.called remains false

How to reproduce

var sinon = require('sinon');

var myObj = {
    prop: 'foo'
  };

var st = sinon.stub(myObj, 'prop').get(function getterFn() {
    return 'bar';
  });

console.log(myObj.prop);
console.log(st.called);
λ node testcase.js
bar
false
Documentation Property accessors

Most helpful comment

For those trying to figure out how to do this, this thread hints at the solution:

const obj = {
    get a() { },
    set a() { },
};

const getterSpy = sinon.spy();
const setterSpy = sinon.spy();

sinon.stub(obj, 'a')
    .get(getterSpy)
    .set(setterSpy);

getterSpy.calledOnce; // false
obj.a;
getterSpy.calledOnce; // true

setterSpy.calledOnce; // false
obj.a = 'foo';
setterSpy.calledOnce; // true

I haven't tried spying on both the getter and setter at the same time, but I know the above works individually.

All 9 comments

This seems to have been like that forever. Not sure if this is a bug or not, as you are not actually calling the stub, but the getter set through the stub. And there are further complications, as you might see from this:

sinon.stub(myObj, 'prop').get(getterFunction).set(setterFunction);
myObj.prop = 1;
myObj.prop;
myObj.prop = 2;

The stub now has two methods. How would we go about this when trying to count callCount, called and friends? Would you mean the stub has been called 1 time, 2 times or 3 times?

I'll close this as a non-bug, but we could perhaps need to clarify this in the docs. Feel free to update them.

How would I mock/stub the value of the property, and check it was called?

I am not sure if a "best" way, but one way (although inelegant) is to make the getter a stub too, and check on that. getterFn=sinon.stub().returns('baz')

Hi, may I have some guidance on how to make this getter a stub?
I'm doing the following.
const createRPCThunk = sinon.stub(webRpcRedux, 'createRPCThunk');
console.log(createRPCThunk) is

{ [Function: proxy]
  isSinonProxy: true,
  formatters:
   { c: [Function: c],
     n: [Function: n],
     D: [Function: D],
     C: [Function: C],
     t: [Function: t],
     '*': [Function: *] },
  reset: [Function: reset],
  invoke: [Function: invoke],
  named: [Function: named],
  getCall: [Function: getCall],
  getCalls: [Function: getCalls],
  calledBefore: [Function: calledBefore],
  calledAfter: [Function: calledAfter],
  calledImmediatelyBefore: [Function: calledImmediatelyBefore],
  calledImmediatelyAfter: [Function: calledImmediatelyAfter],
  withArgs: [Function: withArgs],
  matchingFakes: [Function: matchingFakes],
  matches: [Function: matches],
  printf: [Function: printf],
  calledOn: [Function],
  alwaysCalledOn: [Function],
  calledWith: [Function],
  calledWithMatch: [Function],
  alwaysCalledWith: [Function],
  alwaysCalledWithMatch: [Function],
  calledWithExactly: [Function],
  alwaysCalledWithExactly: [Function],
  neverCalledWith: [Function],
  neverCalledWithMatch: [Function],
  threw: [Function],
  alwaysThrew: [Function],
  returned: [Function],
  alwaysReturned: [Function],
  calledWithNew: [Function],
  alwaysCalledWithNew: [Function],
  callArg: [Function],
  callArgWith: [Function],
  callArgOn: [Function],
  callArgOnWith: [Function],
  throwArg: [Function],
  yield: [Function],
  invokeCallback: [Function],
  yieldOn: [Function],
  yieldTo: [Function],
  yieldToOn: [Function],
  spyCall: { [Function: createSpyCall] toString: [Function: toString] },
  id: 'spy#0',
  called: false,
  notCalled: true,
  calledOnce: false,
  calledTwice: false,
  calledThrice: false,
  callCount: 0,
  firstCall: null,
  secondCall: null,
  thirdCall: null,
  lastCall: null,
  args: [],
  returnValues: [],
  thisValues: [],
  exceptions: [],
  callIds: [],
  errorsWithCallStack: [],
  displayName: 'stub',
  toString: [Function: toString],
  instantiateFake: [Function: create],
  func: { [Function: functionStub] id: 'stub#0' },
  createStubInstance: [Function],
  callsFake: [Function],
  callsArg: [Function],
  callsArgOn: [Function],
  callsArgWith: [Function],
  callsArgOnWith: [Function],
  usingPromise: [Function],
  yields: [Function],
  yieldsRight: [Function],
  yieldsOn: [Function],
  yieldsTo: [Function],
  yieldsToOn: [Function],
  throws: [Function],
  throwsException: [Function],
  returns: [Function],
  returnsArg: [Function],
  throwsArg: [Function],
  returnsThis: [Function],
  resolves: [Function],
  rejects: [Function],
  resolvesThis: [Function],
  callThrough: [Function],
  get: [Function],
  set: [Function],
  value: [Function],
  callsArgAsync: [Function],
  callsArgOnAsync: [Function],
  callsArgWithAsync: [Function],
  callsArgOnWithAsync: [Function],
  yieldsAsync: [Function],
  yieldsRightAsync: [Function],
  yieldsOnAsync: [Function],
  yieldsToAsync: [Function],
  yieldsToOnAsync: [Function],
  create: [Function: create],
  resetBehavior: [Function: resetBehavior],
  resetHistory: [Function: reset],
  onCall: [Function: onCall],
  onFirstCall: [Function: onFirstCall],
  onSecondCall: [Function: onSecondCall],
  onThirdCall: [Function: onThirdCall],
  isPresent: [Function],
  addBehavior: [Function],
  createBehavior: [Function],
  defaultBehavior: null,
  behaviors: [],
  rootObj:
   { createRPCActions: [Getter],
     getActionNamesForRPCId: [Getter],
     getActionNamesForRPCIds: [Getter],
     getActionNamesByType: [Getter],
     listActionNamesForRPCIds: [Getter],
     createRPCThunk: [Getter],
     rpcReducer: [Getter],
     isLoadingReducer: [Getter] },
  propName: 'createRPCThunk',
  restore: [Function: restore] }

How do I stub the getter of createRPCThunk?
I tried const createRPCThunk = sinon.stub().returns('baz');, const createRPCThunk = sinon.stub().returns((a, b, c, d) => 'baz');, and other variations, which are not working. Also createRPCThunk.getCall(0) is null.

@yongish We are trying to keep the issue tracker focused on actual bugs. If you need help with something I suggest you try posting to StackOverflow (remember the sinon tag!) _with a good description of what you want to achieve_ (very hard to understand now). The issue tracker isn't a help forum, but some of us monitor the sinon tag on Stack Overflow and try to answer if we have time.

For those trying to figure out how to do this, this thread hints at the solution:

const obj = {
    get a() { },
    set a() { },
};

const getterSpy = sinon.spy();
const setterSpy = sinon.spy();

sinon.stub(obj, 'a')
    .get(getterSpy)
    .set(setterSpy);

getterSpy.calledOnce; // false
obj.a;
getterSpy.calledOnce; // true

setterSpy.calledOnce; // false
obj.a = 'foo';
setterSpy.calledOnce; // true

I haven't tried spying on both the getter and setter at the same time, but I know the above works individually.

That's interesting. Presumably you can use sinon.stub() instead of spy there too (as it's not really spying on anything: no original methods get run)?

(Thinking aloud...)

In fact, if you were to write
const getterSpy = sinon.spy(obj, 'a'), would you get a _real_ spy that actually spies on the original implementation?

Presumably you can use sinon.stub() instead of spy there too

Yes, if you needed to actually return something, that does work:

const sandbox = createSandbox();
const ss = {};

ss.location = sandbox.stub(window, 'location').value({ search: '?' });
ss.search = sandbox.stub(window.location, 'search').value('?foo=bar');

window.location.search; // ?foo=bar
ss.search.value('?qux=zed');
window.location.search; // ?qux=zed

I couldn't get it to work by passing the sub-stub directly to the value of location:

ss.search = sandbox.stub().value('?foo=bar'); // error
ss.location = sandbox.stub(window, 'location').value({ search: ss.search });

The anon triggers an undefinable error (despite the docs saying anons are supported).

if you were to write const getterSpy = sinon.spy(obj, 'a')

@RoystonS, you would get an error that obj.a is not a function. _If_ it didn't throw, the spy would be useless because obj.a is a property, so there's nothing for a spy to do.

Or, if you wrapped it in the sinon.spy _after_ setting up the stub+spy, that would probably get an error about 'a' being undefined (because obj.a is now a proxy).

Was this page helpful?
0 / 5 - 0 ratings