1

I have an issue that is extremely similar to a previously posted RSpec and Faraday question. To keep things clear, I'm going to borrow that other question's code and make a simple modification that is causing me much sorrow.

class Gist
  def self.create(options)
    post_response = Faraday.post do |request|
      request.url 'https://api.github.com/gists'
      request.headers['Authorization'] = "Basic " + Base64.encode64("#{GITHUB_USERNAME}:#{GITHUB_PASSWORD}")
      request.body = options.to_json
    end

    post_response.body # this is the ONLY modification!
  end
end

The accepted answer works fine for asserting the values of the block, but the spec will fail with a complaint about the code post_response.body.

require 'spec_helper'

describe Gist do
  context '.create' do
    it 'POSTs a new Gist to the user\'s account' do
      gist = {:public => 'true',
              :description => 'a test gist',
              :files => {'test_file.rb' => {:content => 'puts "hello world!"'}}}

      request = double
      request.should_receive(:url).with('https://api.github.com/gists')
      headers = double
      headers.should_receive(:[]=).with('Authorization', "Basic " + Base64.encode64("#{GITHUB_USERNAME}:#{GITHUB_PASSWORD}"))
      request.should_receive(:headers).and_return(headers)
      request.should_receive(:body=).with(gist.to_json)
      Faraday.should_receive(:post).and_yield(request)

      Gist.create(gist)
    end
  end
end

The exact error is: Failures:

1) Gist.create POSTs a new Gist to the user's account
 Failure/Error: post_response.body

 NoMethodError:
   undefined method `body' for #<String:0x007fa5f78f75d8>

I understand what is happening. The yielded rspec block is returning the value of the last line of the block and assigning it to post_response. Unlike the real Faraday, the block is not returning an object that responds to :body.

So, how do I modify the test so that the block returns the mock? I know how to change the original code to make it work; I can just put request as the last line of the block and it will return the mock, but the code I need to test does NOT do that. And I can't get everyone in the firm to modify this particular code style to make it easier to write my tests.

Any clever ideas?

  • why do you have the method return `post_response.body` if you expect the return value to respond to `body`? Unless I'm missing something. – max pleaner Apr 19 '18 at 19:18
  • I think you are missing something. A `Faraday.post` block returns an object that responds to `:body`. I am NOT saying that the return value of the method must also respond to `:body`. – Chuck Remes Apr 19 '18 at 19:27
  • 2
    Stub it like everything else? `Faraday.should_receive(:post).and_yield(request).and_return(something_that_responds_to_body)` – engineersmnky Apr 19 '18 at 21:04
  • @engineersmnky Ah, so obvious! Yes, that works. It honestly never occurred to me that I could chain `and_return()` off of `and_yield`. Thank you for the response... I'll answer my own question here when the system lets me. – Chuck Remes Apr 20 '18 at 13:39

1 Answers1

0

The answer is both simple and obvious.

require 'spec_helper'

describe Gist do
  context '.create' do
    it 'POSTs a new Gist to the user\'s account' do
      gist = {:public => 'true',
              :description => 'a test gist',
              :files => {'test_file.rb' => {:content => 'puts "hello world!"'}}}

      request = double
      request.should_receive(:url).with('https://api.github.com/gists')
      headers = double
      headers.should_receive(:[]=).with('Authorization', "Basic " + Base64.encode64("#{GITHUB_USERNAME}:#{GITHUB_PASSWORD}"))
      request.should_receive(:headers).and_return(headers)
      request.should_receive(:body=).with(gist.to_json)
      Faraday.should_receive(:post).and_yield(request).and_return(double.as_null_object)

      Gist.create(gist)
    end
  end
end

Chain the and_return off of the and_yield to make sure the block returns something specific. Thanks to @engineersmnky for the pointer.