Sinon: Issue retaining prototype properties when using `createStubInstance`

Created on 10 Mar 2016  路  1Comment  路  Source: sinonjs/sinon

Im am trying to stub the stripe api using createStubInstance(), but am unsuccessful with it.

This is how the module is supposed to be used:

  var stripe = require('stripe')('mySecretKey'); // note that the module is a constructor
  stripe.customers.create(...); // the difficulty is that functions are exposed on properties of the prototype

And this illustrates the issue:

  var stripe = require('stripe');
  var stub = sinon.createStubInstance(stripe);
  console.log('This is undefined:  ', stub.customers)
  stub.customers.create.returns(); // Cannot call 'create' on 'undefined'

However, it looks like that the stub is supposed to retain non function values, should it not?
At a closer look of the stripe module, I think it has to do with the way the constructor is written. Here a condensed extract:

var resources = {
  customers: function() {
  }
};

function Stripe(key, version) {
  if (!(this instanceof Stripe)) {
    return new Stripe(key, version);
  }
  this._prepResources();
}

Stripe.prototype = {
  _prepResources: function() {
    for (var name in resources) {
      this[name] = new resources[name](this);
    }
  }
};

My questions are:

  • Is this a bug in sinon? Should this be supported and is it technically possible to do?
  • Is there another way to stub this module using sinon? I know I can use a module to override node's require functionality, but would rather stay away from it if possible.
  • Why can you stub any function on an object, but not the constructor function?

Thanks a lot!

>All comments

It seems you have looked (too) briefly at the code, but not the docs, which also answers your questions. From the docs on sinon.createStubInstance(constructor):

Creates a new object with the given function as the protoype and stubs all implemented functions. The given constructor function is not invoked.

As you see from the code you posted, resources is not on the prototype, hence it will not be stubbed. It is created by the Stripe constructor, which, as the docs clearly say, is not invoked when stubbing it. So Sinon is doing everything it says

Without looking at exactly what you are intending to do, it is hard to say how to do it, but usually it is not a good idea to stub an entire object. From the docs:

Note that it鈥檚 usually better practice to stub individual methods, particularly on objects that you don鈥檛 understand or control all the methods for (e.g. library dependencies). Stubbing individual methods tests intent more precisely and is less susceptible to unexpected behavior as the object鈥檚 code evolves.
If you want to create a stub object of MyConstructor, but don鈥檛 want the constructor to be invoked, use this utility function. var stub = sinon.createStubInstance(MyConstructor)

I'd just do this:

let createStub =  sinon.stub().returns("something");
let stripeStubObj = { customers : { create : createStub } }

doSomethingWith( stripeStubObj );

assert.equals(createStub.callCount, 2);
Was this page helpful?
0 / 5 - 0 ratings

Related issues

JakobJingleheimer picture JakobJingleheimer  路  3Comments

zimtsui picture zimtsui  路  3Comments

stevenmusumeche picture stevenmusumeche  路  3Comments

OscarF picture OscarF  路  4Comments

kbirger picture kbirger  路  3Comments