I am trying to test that 'ApiClient.do_request' is sending the correct arguments and calling 'RestClient::Request.execute.' However, my below specs aren't working as intended. RSpec is unable to properly call 'RestClient::Request.execute,' as the method's 'response' variable keeps coming back as nil, which is why I get the below TypeError when JSON.parse(response) is called. What's strange to me is that I have no issues when walking through the process in Rails console. I'm sure there's something obvious I'm missing here... Any ideas why this is happening, or perhaps suggestions on how I can test this more effectively?
Model:
class ApiClient < ActiveRecord::Base
BASE_PATH = "http://api.bandsintown.com/artists/"
APP_ID = ENV["APP_ID"]
def do_request(method:, base_url:BASE_PATH, app_id:APP_ID, url:, format:"json", options: nil)
response = RestClient::Request.execute(method: method.to_sym,
url: "#{base_url}#{url}.#{format}?api_version=2.0&app_id=#{app_id}#{options}",
timeout: 10)
JSON.parse(response)
end
Spec:
describe ApiClient do
describe "do_request" do
context "when all required arguments are present" do
it "RestClient executes the request" do
@test_client = ApiClient.new
expect(RestClient::Request).to receive(:execute).with(:method=>:get, :url=> "http://api.bandsintown.com/artists/Damien%20Jurado/events/search.json?api_version=2.0&app_id=ShowBoatTest&location=San+Diego,CA&radius=15", :timeout=>10)
@test_client.do_request(method:"get", app_id:"ShowBoatTest",url:"Damien%20Jurado/events/search",options:"&location=San+Diego,CA&radius=15")
end
end
end
RSpec Error:
1) ApiClient do_request when all required arguments are present RestClient executes the request
Failure/Error: JSON.parse(response)
TypeError:
no implicit conversion of nil into String
# /Users/slamflipstrom/.rvm/gems/ruby-2.1.5/gems/json-1.8.3/lib/json/common.rb:155:in `initialize'
# /Users/slamflipstrom/.rvm/gems/ruby-2.1.5/gems/json-1.8.3/lib/json/common.rb:155:in `new'
# /Users/slamflipstrom/.rvm/gems/ruby-2.1.5/gems/json-1.8.3/lib/json/common.rb:155:in `parse'
# ./app/models/api_client.rb:12:in `do_request'
# ./spec/models/api_client_spec.rb:13:in `block (4 levels) in <top (required)>'
Not considering minor syntax errors like api_version=2.0&app_id=ShowBoat
in your ApiClient
class, the problem seems to be caused by your RestClient::Request.execute
request returning nil
.
Debug or inspect the value of response
and make sure it is not nil
.
class ApiClient < ActiveRecord::Base
BASE_PATH = "http://api.bandsintown.com/artists/"
APP_ID = ENV["APP_ID"]
def do_request(method:, base_url:BASE_PATH, app_id:APP_ID, url:, format:"json", options: nil)
response = RestClient::Request.execute(method: method.to_sym,
url: "#{base_url}#{url}.#{format}?api_version=2.0&app_id=#{app_id}#{options}",
timeout: 10)
puts response.inspect
response ? JSON.parse(response) : []
end
On the other hand, you are testing ApiClient
class's implementation. In other words, you're testing that the class is internally calling some other class in specific manner. If you decide to replace the RestClient with HTTParty for example at some later time, your tests will fail (even though they shouldn't, because your class is still working correctly).
So, testing the class's implementation makes your tests "fragile". Instead, give an input and test for the expected correct output.
describe ApiClient do
context "with all required arguments" do
describe "#do_request" do
it "returns object in correct format" do
response = subject.do_request(method:"get", app_id:"ShowBoatTest",url:"Damien%20Jurado/events/search",options:"&location=San+Diego,CA&radius=15")
expect(response.body).to include(title: "Damien Jurado @ The Casbah in San Diego, CA")
# etc.
end
end
end
end