How to test external APIs with Webmock and VCR in RSpec
Tags: rails, api, testing, rspec
Published: 2023-10-14 14:08:53

Let’s talk about how we can test **external APIs** in our applications using Webmock and VCR, for this post, I‘ll use the RSpec suite. I’ll be very straightforward and minimalist in this explanation

💎 *The examples will be done in a Rails app*

## **🌐 Webmock**

The [webmock](https://github.com/bblimke/webmock) gem allows us to create stubs for HTTP requests, in other words, you can create fake returns for some internet service, without making the real request.

### Setting

Adding to gem file

```ruby
group :test do
  gem "webmock"
end
```

Inside the `spec/scpec_helper.rb` file, add the following `require`.

```ruby
require 'webmock/rspec'
```

Run the bundle install to install the gem

```ruby
bundle install
```

With this, the HTTP requests are disabled and the Webmock will always request the stubs of the responses, but making these stubs for all APIs can be tired, so for this, we will use VCR, which will do this “automatically” for us, so let’s see how we can do this.

## 📼 VCR

The [VCR gem](https://github.com/vcr/vcr) records the interaction of API calls for future usage, in other words, VCR creates a stub for us, making the real call only once and recording it in a file, to use for the next calls without making the real call again.

### Setting

Adding to Gemfile

```ruby
group :test do
  gem "vcr"
end
```

Adding the code block inside of `spec/scpec_helper.rb` :

```ruby
VCR.configure do |config|
  config.cassette_library_dir = "spec/fixtures/cassettes"
  config.hook_into :webmock
  config.configure_rspec_metadata!
end
```

The property `cassette_library_dir` is about the path where the result of the APIs will be recorded, the `hook_into` is about Webmock (which we talked about in the previous topic) to disable the HTTP requests when necessary and the `configure_rspec_metadata!` allow us to use a property in our RSpec tests to keep them more simple using VCR, when we look at the code it will be more clear.

## 👨‍💻Testing

To show all the things I’ll make a simple get API call to **JSON Placeholder** in my test.

```ruby
describe 'ServiceApi' do
  it 'get a post', :vcr do
    response = Net::HTTP.get('jsonplaceholder.typicode.com', '/posts/1')
    expect(response.code).to eq '200'
end
```

Note that in the `it`, I just add a `:vcr` argument, basicly with this I already informed the RSpec that the test will use the VCR, In case you had a multiple test, it’s possible add the :vcr argument directly in the `describe`.

When we ran the test, we have the following situation:

```ruby
1 example, 0 failures
```

It passes as expected, note that a file was generated in the path that we set in `cassette_library_dir` property with the name of the test, it shows that the VCR made a request and recorded the response in an yml file, now, the next time that this test runs, the RSpec will get the data from this file instead made a real API call, in case you need regenerate the response, just delete the file and rerun the test.

Cool, right? This is a very simple way to work with external APIs in our tests and ensure that we will not have intermittent fails.