如何使用 Ruby 和 RSpec 测试 HTTParty API 调用

How to test HTTParty API call with Ruby and RSpec

我正在使用 HTTParty gem 调用 GitHub API 来访问用户的存储库列表。

这是一个使用 Sinatra 的非常简单的应用程序,它根据用户存储库中出现的最常见语言显示用户最喜欢的编程语言。

我对如何编写一个 RSpec 期望来模拟实际的 API 调用而只是检查是否返回了 json 数据有些困惑。

我有一个模拟 .json 文件,但不确定如何在我的测试中使用它。

有什么想法吗?

github_api.rb

require 'httparty'

class GithubApi
  attr_reader :username, :data, :languages

  def initialize(username)
    @username = username
    @response = HTTParty.get("https://api.github.com/users/#{@username}/repos")
    @data = JSON.parse(@response.body)
  end
end

github_api_spec.rb

require './app/models/github_api'
require 'spec_helper'

describe GithubApi do
  let(:github_api) { GithubApi.new('mock_user') }

  it "receives a json response" do

  end
end

为清楚起见,其余文件:

results.rb

require 'httparty'
require_relative 'github_api'

class Results

 def initialize(github_api = Github.new(username))
   @github_api = github_api
   @languages = []
 end

 def get_languages
   @github_api.data.each do |repo|
     @languages << repo["language"]
   end
 end

 def favourite_language
   get_languages
   @languages.group_by(&:itself).values.max_by(&:size).first
 end
end

application_controller.rb

require './config/environment'
require 'sinatra/base'
require './app/models/github_api'

class ApplicationController < Sinatra::Base

  configure do
    enable :sessions
    set :session_secret, "@3x!ilt£"
    set :views, 'app/views'
  end

  get "/" do
    erb :index
  end

  post "/user" do
    @github = GithubApi.new(params[:username])
    @results = Results.new(@github)
    @language = @results.favourite_language
    session[:language] = @language
    session[:username] = params[:username]
    redirect '/results'
  end

  get "/results" do
    @language = session[:language]
    @username = session[:username]
    erb :results
  end

run! if app_file == [=16=]

end

您可以使用 https://github.com/bblimke/webmock and send back mock.json using webmock. This post, https://robots.thoughtbot.com/how-to-stub-external-services-in-tests 模拟那些 API 调用,引导您使用 RSpec 完成 webmock 的设置(post 模拟 GitHub 中的测试API也叫)

有多种方法可以解决这个问题。

您可以像@anil 所建议的那样,使用像 webmock 这样的库来模拟底层 HTTP 调用。您还可以使用 VCR (https://github.com/vcr/vcr) 做一些类似的事情,它记录对 HTTP 端点的实际调用的结果,并在后续请求中回放该响应。

但是,考虑到你的问题,我不明白你为什么不能只使用 Rspec double。我会告诉你如何在下面。但是,首先,如果代码不全部在构造函数中,测试代码会更容易一些。

github_api.rb

require 'httparty'

class GithubApi
  attr_reader :username

  def initialize(username)
    @username = username
  end

  def favorite_language
    # method to calculate which language is used most by username
  end

  def languages
    # method to grab languages from repos
  end

  def repos
    repos ||= do
      response = HTTParty.get("https://api.github.com/users/#{username}/repos")
      JSON.parse(response.body)
    end
  end
end

请注意,您不需要在 url 中引用 @username 变量,因为您有一个 attr_reader.

github_api_spec.rb

require './app/models/github_api'
require 'spec_helper'

describe GithubApi do
  subject(:api) { described_class.new(username) }

  let(:username) { 'username' }

  describe '#repos' do
    let(:github_url) { "https://api.github.com/users/#{username}/repos" }
    let(:github_response) { instance_double(HTTParty::Response, body: github_response_body) }
    let(:github_response_body) { 'response_body' }

    before do
      allow(HTTParty).to receive(:get).and_return(github_response)
      allow(JSON).to receive(:parse)

      api.repos
    end

    it 'fetches the repos from Github api' do
      expect(HTTParty).to have_received(:get).with(github_url)
    end

    it 'parses the Github response' do
      expect(JSON).to have_received(:parse).with(github_response_body)
    end
  end
end

请注意,无需实际加载或解析任何真实的 JSON。我们在这里测试的是我们进行了正确的 HTTP 调用并且我们在响应中调用了 JSON.parse。开始测试 languages 方法后,您需要实际加载和解析测试文件,如下所示:

let(:parsed_response) { JSON.parse(File.read('path/to/test/file.json')) }