Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Having trouble with WebMock, not stubbing correctly

Ruby 1.9.3, RSpec 2.13.0, WebMock 1.17.4, Rails 3

I am writing tests for a company app. The controller in question displays a table of a customer's placed calls, and allows for sort/filter options.

EDIT The test fails because with my current setup, the path does not render, because the recorder_server is either not running locally, OR not setup correctly. Please help with this, too.

A Errno::ECONNREFUSED occurred in recordings#index:
Connection refused - connect(2)
/usr/local/lib/ruby/1.9.1/net/http.rb:763:in `initialize'

-------------------------------
Request:
-------------------------------
* URL       : http://www.recorder.example.com:8080/recorded_calls
* IP address: 127.0.0.1
* Parameters: {"controller"=>"recordings", "action"=>"index"}
* Rails root: /var/www/rails/<repository>
  1. As a call is placed, its data joins an xml file, created by an external API, called Recorder
  2. The RecordingsController takes the xml file, and parses it into a hash.
  3. When you visit the associated path, you see the results of the hash -- a table of placed calls, their attributes, and parameters for sort/filter.

Here is my spec so far.

require 'spec_helper'
include Helpers

feature 'Exercise recordings controller' do
  include_context "shared admin context"

  background do
    canned_xml = File.open("spec/support/assets/canned_response.xml").read
    stub_request(:post, "http://recorder.example.com:8080/recorder/index").
      with(body: {"durations"=>["1"], "durations_greater_less"=>["gt"], "filter_from_day"=>"29", "filter_from_hour"=>"0", "filter_from_minute"=>"0", "filter_from_month"=>"12", "filter_from_year"=>"2014", "filter_prefix"=>true, "filter_to_day"=>"29", "filter_to_hour"=>"23", "filter_to_minute"=>"59", "filter_to_month"=>"12", "filter_to_year"=>"2014"}, # "shared_session_id"=>"19f9a08807cc70c1bf41885956695bde"},
           headers: {'Accept'=>'*/*', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Ruby'}).
      to_return(status: 200, body: canned_xml, headers: {})
    uri = URI.parse("http://recorder.example.com:8080/recorder/index")
    visit recorded_calls_path
  end

  scenario 'show index page with 1 xml result' do
    #page.save_and_open_page
    expect(title).to eq("Recorded Calls")
  end
end

And here is the RecordingsController

class RecordingsController < ApplicationController
  # before_filter options
  def index
    test_session_id = request.session_options[:id]
    #Make request to recording app for xml of files
    uri = URI.parse("http://#{Rails.application.config.recorder_server}:#{Rails.application.config.recorder_server_port}/recorder/index")
    http = Net::HTTP.new(uri.host, uri.port)
    xml_request = Net::HTTP::Post.new(uri.request_uri)
    xml_request_data = Hash.new
    # sorting params
    xml_request_data[:shared_session_id] = request.session_options[:id]
    xml_request.set_form_data(xml_request_data)
    response = http.request(xml_request)
    if response.class == Net::HTTPOK
      @recordings_xml = XmlSimple.xml_in(response.body)
      @recordings_sorted = @recordings_xml["Recording"].sort { |a,b| Time.parse("#{a["date"]} #{a["time"]}") <=> Time.parse("#{b["date"]} #{b["time"]}") } unless @recordings_xml["Recording"].nil?
    else @recordings_xml = Hash.new
    end
  end
  # other defs
end

Any and all advice is much appreciated. Thank you.

like image 555
onebree Avatar asked Dec 26 '14 21:12

onebree


2 Answers

How I configured WebMock

I am answering my own question, with the help of B-Seven and a string of comments. File by file, I will list the changes made in order to properly use WebMock.

  1. Add WebMock to Gemfile under group :test, :development.
    • bundle install to resolve dependencies
    • my current setup included Ruby 1.9.3, Rails 2.13.0, WebMock 1.17.4
  2. Setup spec_helper.rb to disable "Real HTTP connections". (This was a backtrace error received later on in this puzzling process.) This allows, to my understanding, all "real connections" to translate into localhost connections and work offline... Which is great since, ideally, I do not want the external app's server to run simultaneously.

    require 'webmock/rspec'
    WebMock.disable_net_connect!(allow_localhost: true)
    
  3. In my test.rb environment file, the configurations for recorder_server and port were commented out... If left uncommented, the controller would raise an exception stating uninitialized constants. I used the test server/port (substituting the company name for example) as my layout for the spec stubbing.

  4. In recordings_controller_spec.rb, I had already figured out how to make a canned XML response. With these changes above, my spec was able to correctly stub a response on an external, secondary app, and use such response to correctly render the view associated with the controller being tested.

    require 'spec_helper'
    include Helpers
    
    feature "Exercise recordings_controller" do
      include_context "shared admin context"
    
      # A background is currently not used, because I have 3 scenario types... No xml
      # results, 1 result, and 2 results. I will later DRY this out with a background,
      # but the heavy lifting is over, for now.
    
      scenario "show index page with 1 xml result" do
        canned_xml_1 = File.open("spec/support/assets/canned_response_1.xml").read
        stub_request(:post, "http://recorder.example.com:8080/recorder/index").
          with(headers: {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
          to_return(status: 200, body: canned_xml_1, headers: {})
        uri = URI.parse("http://recorder.example.com:8080/recorder/index")
        visit recorded_calls_path
        title.should == "Recorded Calls"
        page.should have_content("Search Results")
        page.should have_content("Inbound", "5551230000", "175", "December 24 2014", "12:36:24", "134")
      end
    
    end
    

Advice/Resources that helped

  • With B-Seven's suggestion to my original question (see revisions), I was initially stubbing localhost:3000. He said this was incorrect. After further research, I agree since stubbing with WebMock is typically reserved for outside http connections.
  • In comments after his answer, B-Seven listed articles to refer to. I will list the ones that helped me the most.
    • http://robots.thoughtbot.com/how-to-stub-external-services-in-tests
    • http://railscasts.com/episodes/275-how-i-test
    • https://github.com/bblimke/webmock
    • http://www.agileventures.org/articles/testing-with-rspec-stubs-mocks-factories-what-to-choose
  • It is very important to read the backtrace generated from an errors. What took me so long to figure out how to mock was mainly reading them incorrectly. As you can see from my question, I was making a :get stub request. A coworker pointed out that the backtrace suggested to use :post. That was the final piece to make my spec pass.
  • I decided not to input the configuration variables as my stub request, for it would result in long lines of code. Instead, this is why I needed to uncomment out those configurations in test.rb.
like image 133
onebree Avatar answered Sep 28 '22 21:09

onebree


Why are you stubbing localhost? I think you want to

stub_request(:get, "http://#{Rails.application.config.recorder_server}:#{Rails.application.config.recorder_server_port}/recorder/index").
like image 43
B Seven Avatar answered Sep 28 '22 22:09

B Seven