In the article [Ruby] Procedure for creating an external API linkage class using Faraday, we introduced how to link with an external API using Faraday.
In logic test code that uses an external API, it is common to stub HTTP requests instead of using the actual API. In Rails, you can stub HTTP requests by using a gem called webmock.
This time, I will introduce how to use WebMock to create logic test code linked with an external API.
This time, we will create a test code for a class called QiitaClientAPI that works with the Qiita API.
The class implements a method called get_items to get a list of articles from Qiita.
The specific source code is as follows.
lib/qiita_api_client.rb
class QiitaApiClient
  class HTTPError < StandardError
    def initialize(message)
      super "connection failed: #{message}"
    end
  end
  class << self
    def connection
      Faraday::Connection.new('https://qiita.com') do |builder|
        builder.authorization :Bearer, "#{Rails.application.credentials.qiita[:token]}"
        builder.request  :url_encoded #URL-encode request parameters
        builder.response :logger #Output the response to standard output
        builder.adapter Faraday.default_adapter #Adapter selection. The default is Net::HTTP
        builder.response :json, :content_type => "application/json" #JSON parse the response body
      end
    end
    def get_items
      begin
        response = connection.get(
          '/api/v2/items'
        )
        response.body
      rescue Faraday::ConnectionFailed => e
        raise QiitaApiClient::HTTPError.new(e.message)
      end
    end
  end
end
Prepare to run the test code.
This time we will use RSpec as the testing framework. Add rspec-rails to your Gemfile.
Gemfile
group :development, :test do
  gem 'rspec-rails'
end
Install gem and set up RSpec.
$ bundle install
$ rails generate rspec:install
Add webmock to your Gemfile and install it.
Gemfile
gem 'webmock'
$ bundle install
Below, we will introduce how to write test code using WebMock.
The normal test code is as follows.
spec/lib/qiita_api_client_spec.rb
require 'rails_helper'
describe 'QiitaApiClient' do
  before do
    WebMock.enable! #Enable WebMock
    allow(Rails.application.credentials).to receive(:qiita).and_return({token: '123'})
  end
  describe '.get_items' do
    let(:response_body) { [{ "title": "test" }].to_json }
    before do
      WebMock.stub_request(:get, "https://qiita.com/api/v2/items").
        with(
          headers: {'Authorization' => "Bearer 123"}
        ).
        to_return(
          body: response_body,
          status: 200,
          headers: { 'Content-Type' => 'application/json' }
        )
    end
    it 'Data can be obtained' do
      expect(QiitaApiClient.get_items).to eq(JSON.parse(response_body))
    end
  end
end
I have enabled WebMock with WebMock.enable! and created an HTTP request stub at WebMock.stub_request.
By using WebMock, you can describe the behavior of "what kind of request is returned and what kind of response is returned". This allows you to create a simulated situation where data is exchanged via an external API.
In addition, the part of allow (Rails.application.credentials) described in before sets a dummy value for Rails.application.credentials.qiita [: token] described in the application code. Is for.
If the application code cannot communicate normally with the external API, it raises the exception QiitaApiClient :: HTTPError with an error message.
The test code of the abnormal system that confirms that the exception has occurred is as follows.
spec/lib/qiita_api_client_spec.rb
require 'rails_helper'
describe 'QiitaApiClient' do
  before do
    WebMock.enable!
    allow(Rails.application.credentials).to receive(:qiita).and_return({token: '123'})
  end
  describe '.get_items' do
    let(:response_body) { { "message": "error_message", "type": "error_type" }.to_json }
    before do
      WebMock.stub_request(:get, "https://qiita.com/api/v2/items").
        to_raise(Faraday::ConnectionFailed.new("some error"))
    end
    it 'Exception is raised' do
      #Exception test is expect()Not expect{}So be careful
      expect{QiitaApiClient.get_items}.to raise_error(QiitaApiClient::HTTPError, "connection failed: some error")
    end
  end
end
I have created a stub that raises an exception with to_raise in WebMock and confirmed that the exception is raised with raise_error in expect.
The final test code that summarizes the normal system and abnormal system introduced this time is as follows.
spec/lib/qiita_api_client_spec.rb
require 'rails_helper'
describe 'QiitaApiClient' do
  let(:qiita_base_uri) { 'https://qiita.com/api/v2' }
  before do
    WebMock.enable!
    allow(Rails.application.credentials).to receive(:qiita).and_return({token: '123'})
  end
  describe '.get_items' do
    subject { QiitaApiClient.get_items }
    context 'success' do
      let(:response_body) { [{ "title": "test" }].to_json }
      before do
        WebMock.stub_request(:get, "#{qiita_base_uri}/items").
          with(
            headers: {'Authorization' => "Bearer 123"}
          ).
          to_return(
            body: response_body,
            status: 200,
            headers: { 'Content-Type' => 'application/json' }
          )
      end
      it 'Data can be obtained' do
        expect(subject).to eq(JSON.parse(response_body))
      end
    end
    context 'Failure' do
      before do
        WebMock.stub_request(:get, "#{qiita_base_uri}/items").
          to_raise(Faraday::ConnectionFailed.new("some error"))
      end
      it 'Exception is raised' do
        expect{subject}.to raise_error(QiitaApiClient::HTTPError, "connection failed: some error")
      end
    end
  end
end
We will introduce how to use WebMock to create logic test code linked with an external API.
The flow of the test code using WebMock is as follows.
I'm on Twitter (@ nishina555). I hope you will follow me!
Recommended Posts