Unit Testing Node.js apps and Stripe API integration

This tutorial appeared in node weekly.

Stripe maintains a handful of official API libraries, they are simple and equipped with language-specific examples so you can easily integrate them into your own software product.

Depending on your application a successful payment has different implications, but it's always a good idea to make sure that the user gets what he's paid for.

In the Node-Stripe API world this means that the callback which is executed after a payment has been made behaves correctly in various scenarios. To make sure that it does, let's cover it with some unit tests.

This tutorial is about testing your Stripe driven code, if you're still unfamiliar with stripe I suggest you to read the Node.js Stripe API Reference.

Let's say we built an application where the users can order hand-made cosmetics through your web-shop. Here is the module which handles all the Stripe related stuff:

var stripe = require('stripe')(
  'sk_test_your_stripe_test_key'
);

module.exports = {

  createOrder: function (req, cb) {
    var items = req.items,
        email = req.email,
        errors = [];

    // Always validate the user input before handling it to an external API.
    // If you can detect requests that would fail anyway, you'll spare the
    // cost of the network request before it's even made.
    if (!items || items.length < 1) {
      // return cb('No items found.', null);
      errors.push('No items found.');
    }

    // a terrible way to validate an email address
    if (!email) {
      errors.push('Email is required.');
    }

    if (errors.length > 0) {
      cb(errors, null);
    } else {
      stripe.orders.create({
        currency: 'usd',
        items: items,
        email: email
      }, function(err, order) {
        // stripe response, asynchronously called
        if (err) {
          cb(err, null);
        } else {
          if (!order || !order.id) {
            cb('Unknown error occurred.', null);
          } else {
            cb(null, order);
          }
        }
      });
    }
  }
});

stripe.orders.create returns an order object if the call succeeded and err otherwise.

Don't forget that we don't want to test the correctness of the Stripe API itself only the behavior of its callback method. We are going to use Sinon.JS to stub the real Stripe API methods and return fake responses to our module.

One thing that won't be obvious when you start stubbing Stripe methods is that create isn't a property of the orders object. That's because Orders extends StripeResource so the order methods are really in orders.proto:

this.sandbox.stub(stripeClient.orders.__proto__, 'create', func);

The above stub will prevent the real orders.create from being called and gives you control over triggering its callback with arbitrary parameters.

Let's start by implementing the best-case scenario, when the Stripe API executes our callback with a newly created order that contains only an id and with no errors:

this.sandbox.stub(stripeClient.orders.__proto__, 'create', function (orderRequest, func) {
  func(null, {id: 123});
});

Go back to our module we can see what happens in this case:

if (!order || !order.id) {
  cb('Unknown error occurred.', null);
} else {
  cb(null, order);
}

Since our order was {id: 123} the cb callback passed to createOrder should be called with null, order.

To prove this just call module.createOrder in your favorite test suite and see if the callback has the expected parameters:

stripeConnector.createOrder(request, function (err, order) {
  assert.isNull(err);
  assert.isNotNull(order.id);
  assert.equal(123, order.id);
});

For complete code and running tests the git repository: node-stripe-api-testing-tutorial.

Leave a comment below and let me know what you think!

Did you find this article valuable?

Support Ákos Kőműves by becoming a sponsor. Any amount is appreciated!