Testing function contains an API request

181 Views Asked by At

I'm trying to test my rails application which using Stripe APIs, So I started with models, I'm using Rspec, The model which i want to test is called bank_account.rb inside it there is a function called (create_bank_account) with argument (bank_token) its pseudocode is something like this:

def create_bank_account(bank_token)
  # make a Stripe request and save it in local variable
  # save needed data in my bank_account table in my DB
end

when i started to test this function, I found that there is an API call inside it, Which is not good, I need my test not to depend on Internet, So after searching I found 'StripeMock` gem, It is useful and i started to use it with Rspec, but I found my self writing a test like this:

it 'with valid bank_token` do
  # create a double for bank_account
  # using StripeMock to get a faked response for creating
  # new bank_account
  # expect the doube to receive create_bank_account
  # function and response with saving the data inside the DB
end

but after writing this I noticed that I didn't actually run create_bank_account function i faked it, So my questions are:

1- How can i test function that includes API request but run the function it self not faking it?

2- I read a lot about when we use doubles and stubs and what i understood is when a function is not completed, but if the functions is already implemented should i use doubles to avoid something like functions that call APIs?

1

There are 1 best solutions below

7
Tom Lord On BEST ANSWER

First and foremost:

  • Do not create a double for bank_account.
  • Do not mock/stub bank_account.create_bank_account.

If you do either of these things, in a test that is supposed to be testing behaviour of BankAccount#create_bank_account, then your test is worthless.

(To prove this point, try writing broken code in the method. Your tests should obviously fail. But if you're mocking the method, everything will remain passing!!)

One way or another, you should only be mocking the stripe request, i.e. the behaviour at the boundary between your application and the internet.

I cannot provide a working code sample without a little more information, but broadly speaking you could refactor your code from this:

def create_bank_account(bank_token)
  # make a Stripe request and save it in local variable
  # save needed data in my bank_account table in my DB
end

To this:

def create_bank_account(bank_token)
  stripe_request = make_stripe_request(bank_token)
  # save needed data in my bank_account table in my DB
end

private

def make_stripe_request(bank_token)
  # ...
end

...And then in your test, you can use StripeMock to only fake the response of BankAccount#make_stripe_request.

If the code is not so easy to refactor(?!), then stubbing the Stripe library directly like this might not be practical. An alternative approach you can always take is use a library like webmock to simply intercept all HTTP calls.