Jason Leach
Jason Leach

Reputation: 4229

Mock stripe with Jest

I'd like to mock the node Stripe SDK in Jest because I don't want to run the mock API server from Stripe but I can't figure how how to do it. I'm creating a __mocks__ directory and adding stripe.js but I can't get anything usable to export.

I typically get TypeError: Cannot read property 'create' of undefined when calling strypegw.charges.create(). I'm using ES6 module syntax so I import stripe from 'stripe'.

Upvotes: 18

Views: 14562

Answers (4)

Matt Lo
Matt Lo

Reputation: 5731

As of [email protected], you can add a direct spyOn to the Customer class instead of overriding the entire Stripe class.

Short answer:

jest.mock('stripe', () => {
  const stripe = jest.requireActual('stripe');
  jest.spyOn(stripe.resources.Customers.prototype, 'create')
    .mockImplementation(() => (
      Promise.resolve({id: 'stripe-test-id'})
    ));
  return stripe;
})

Long answer:

  1. You don't want to override the main Stripe class because there are a lot of static values and utility helpers your application might use such as Stripe.errors.StripeError. It would be superfluous to mock things that don't need mocking.
  2. The reason why Stripe.prototype.customer doesn't work because .customer is not a method until after instantiation. See https://github.com/stripe/stripe-node/blob/master/lib/stripe.js#L124
  3. Luckily Stripe exposes their resources statically via Stripe.resources. So you can spy on the resources directly. When the real Stripe gets instantiated, it'll reference the spies instead of the real class for the particular resource you're trying to mock. See https://github.com/stripe/stripe-node/blob/master/lib/stripe.js#L51
  4. A quick and dirty way to mock an entire resource is to override the resource references like this: Stripe.resources.Customers = {create: jest.fn()};. I would not recommend it for reasons stated in point 1.

Upvotes: 5

Oleg Abrazhaev
Oleg Abrazhaev

Reputation: 2839

Add this in a helper function and invoke it in jest setup file or at the top of the test when required.

// Mocking Stripe object
  const elementMock = {
    mount: jest.fn(),
    destroy: jest.fn(),
    on: jest.fn(),
    update: jest.fn(),
  };

  const elementsMock = {
    create: jest.fn().mockReturnValue(elementMock),
  };

  const stripeMock = {
    elements: jest.fn().mockReturnValue(elementsMock),
    createToken: jest.fn(() => Promise.resolve()),
    createSource: jest.fn(() => Promise.resolve()),
  };

  // Set the global Stripe
  window.Stripe = jest.fn().mockReturnValue(stripeMock);

With this approach, you can easily test your stripe related code as well

// Ex. of a token successfully created mock
  stripeMock.createToken.mockResolvedValue({
    token: {
      id: 'test_id',
    },
  });

  // Ex. of a failure mock
  stripeMock.createToken.mockResolvedValue({
    error: {
      code: 'incomplete_number',
      message: 'Your card number is incomplete.',
      type: 'validation_error',
    },
  });

Upvotes: 0

JeromeBu
JeromeBu

Reputation: 1159

Here is a simple solution :

jest.mock("stripe", () => {
  return jest.fn().mockImplementation(function {
    return {
      charges: {
        create: () => "fake stripe response",
      },
    };
  });
});

I found it in jest documentation about ES6 Class Mocks

Upvotes: 2

Sergey Nakhankov
Sergey Nakhankov

Reputation: 350

// your-code.js
const stripe = require('stripe')('key');
const customer = await stripe.customers.create({
    ...
});

// __mocks__/stripe.js
class Stripe {}
const stripe = jest.fn(() => new Stripe());

module.exports = stripe;
module.exports.Stripe = Stripe;

// stripe.tests.js
const { Stripe } = require('stripe');
const createCustomerMock = jest.fn(() => ({
    id: 1,
    ...
}));
Stripe.prototype.customers = {
    create: createCustomerMock,
};

Upvotes: 16

Related Questions