Mock HTTP using VCR with RSpec | Mock Authy | Rails

Well, slower test suite is a nightmare both in your local machine and in your CI server. If your gem provider has built some Mocking APIs then its cool. But, no mocking API and you are writing Unit Test for your models and controller then beware, do not leave your app making HTTP requests everytime.

What to do?

If you cannot mock your library then mock the damn Internet. Simple enough! you can get going in a couple of minutes.

You are right! we will be using VCR to record the HTTP responses and replay the recordings using Webmock. You can use any other tools like

Configurations

# in Gemfile
group :test do
  gem 'webmock'
  gem 'vcr'
end
# spec/support/vcr_config.rb
VCR.configure do |c|
  # This directory will hold all of your recordings
  c.cassette_library_dir = 'spec/vcr'

  # Your HTTP request service. You can also use fakeweb, webmock, and more
  c.hook_into :webmock
end

How to use?

VCR.use_cassette('cassette_group/cassette_name') do
   # the body of the test would go here...
end

Example:

it 'should auto update the authy_id just after phone_number is updated' do
  old_authy_id = user.authy_id
  VCR.use_cassette('models/users/authy_id') do
    user.update(first_name: 'New updated name', phone_number: '3779345998')
  end
  expect(user.reload.authy_id).not_to eq(old_authy_id)
end

When you first time run this example, you see this new folder structure models/users/authy_id.yml  generated inside spec/vcr directory.

Mocking Authy

In my case I have set a after_save callback like

# In models/user.rb
after_save :update_authy_id, if: ->{phone_number_changed? || country_code_changed?}

private

def update_authy_id
  authy = Authy::API.register_user(
      email:        email,
      cellphone:    phone_number,
      country_code: country_code
  )
  self.update(authy_id: authy.id) if authy.present?
end

In that case, I needed to wrap all my create(:factory) calls with in VCR block. like

let(:user) do
  VCR.use_cassette('models/users/authy_id') do
    create(:user)
  end
end

Caution!

If you are using Faker like gems to generate random data which are going to be used in HTTP parameters then you should skip. Like

Avoid:

# In factory
phone_number { Faker::PhoneNumber.cell_phone }

Use this if possible.

country_code { ['1', '977', '61'].sample }
phone_number { ['9X4X433445','2322334454'].sample }

Reason: In former implementation, there were thousands of possible outcome and I can guarantee that it’s not possible to record every combination by VCR. In later one, there are only two combinations and they can easily be recorded.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s