如何使用 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')) }
我正在使用 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')) }