Stubbing stripe with sinon - using stub.yields
It looks like you're trying to isolate your #post() function from #stripe.customers.create(), in the Stripe API. @lambinator is correct in pointing out that the customers object is created, dynamically, when you call
require('stripe')('my-key-from-config')
and
require('stripe')('test')
so your stub, in the test, does not apply to #stripe.customers.create(), in the controller.
You could inject the test instance of stripe into the controller, as @lambinator suggests. Injection is pretty much the best thing ever. However, if you're writing a rubber-meets-the-road type component (like a proxy), injection is inappropriate. Instead, you can use the second export provided in the Stripe module:
Stripe.js:
...
// expose constructor as a named property to enable mocking with Sinon.JS
module.exports.Stripe = Stripe;
Test:
var sinon = require('sinon');
var stripe = require('stripe')('test');
var StripeObjectStub = sinon.stub(Stripe, 'Stripe', function(){
return stripe;
});
//NOTE! This is required AFTER we've stubbed the constructor.
var controller = require('./controller');
var stub = sinon.stub(stripe.customers, 'create');
stub.create.yields([null, {id: 'xyz789'}]);
//stub.create.yields(null, {id: 'xyz789'}); //same result with or without array
controller.post({}, {}, function(){});
Controller:
require('stripe').Stripe('my-key-from-config');
var controller = {
post: function (req, res, done) {
stripe.customers.create({
card: req.body,
plan: 'standard1month',
}, function(err, customer) {
console.log('ERR = ', err);
console.log('CUSTOMER = ', customer);
});
}
Then #stripe.customers.create(), in your controller, will invoke your test stub.
Based on @Angrysheep's answer which uses the exported Stripe constructor (the best method IMHO), here's a working code as of the time of writing this:
Controller
//I'm using dotenv to get the secret key
var stripe = require('stripe').Stripe(process.env.STRIPE_SECRET_KEY);
Test
//First, create a stripe object
var StripeLib = require("stripe");
var stripe = StripeLib.Stripe('test');
//Then, stub the stripe library to always return the same object when calling the constructor
const StripeStub = sinon.stub(StripeLib, 'Stripe').returns(stripe);
// import controller here. The stripe object created there will be the one create above
var controller = require('./controller');
describe('a test', () => {
let createCustomerStub;
const stripeCustomerId = 'a1b2c3';
before(() => {
//Now, when we stub the object created here, we also stub the object used in the controller
createCustomerStub = sinon.stub(stripe.customers, 'create').returns({id: stripeCustomerId});
});
after(() => {
createCustomerStub.restore();
});
});
EDIT: based on a comment below, this approach might no longer work.
A reasonable approach to walk around this entire issue is to use Stripe as an injected dependency, i.e. to pass the Stripe object to the relevant object's constructor. This will allow injecting a stub as part of the testing suite.
When you do this:
var stripe = require('stripe')('my-key-from-config');
The stripe library creates the customer
and other objects dynamically. So, when you stub it out in one file:
test.js
var stripe = require('stripe')('test');
var stub = sinon.stub(stripe.customers, 'create');
And your controller creates another stripe
instance to use in another file:
controller.js
var stripe = require('stripe')('my-key-from-config');
var controller = { ... }
The stubbed version from test has no impact on the controller's version.
SO....
You either need to inject the test instance of stripe
into your controller, or use a library like nock to mock things at the http level, like so:
nock('https://api.stripe.com:443')
.post('/v1/customers', "email=user1%40example.com&card=tok_5I6lor706YkUbj")
.reply 200,
object: 'customer'
id: 'cus_somestripeid'