5

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.

onebree
  • 1,853
  • 1
  • 17
  • 44

2 Answers2

8

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

Community
  • 1
  • 1
onebree
  • 1,853
  • 1
  • 17
  • 44
1

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").
B Seven
  • 44,484
  • 66
  • 240
  • 385
  • Thank you. The `test.rb` file has these configurations commented out. Do i need to define them in that file before stubbing? – onebree Dec 27 '14 at 12:08
  • It looks like the config is set up correctly. It is making the request to example.com, which is good. So you should stub_request example.com or `Rails.application.config.recorder_server`. I think the latter is better. – B Seven Dec 27 '14 at 14:38
  • However, WebMock does not seem to be working. It looks like you need to use `WebMock.disable_net_connect!`. Which version of WebMock are you using? – B Seven Dec 27 '14 at 14:39
  • It looks like the WebMock version is OK. You need to use `disable_net_connect!` and `stub_request` before running the spec. First `disable_net_connect!` and it should tell you the HTTP request it wants to make. Then you will know exactly what to stub. – B Seven Dec 29 '14 at 15:32
  • In `spec_helper.rb`, I have `WebMock.disable_net_connect!(allow: "http://recorder.example.com:8080")`. I am getting `A WebMock::NetConnectNotAllowedError occurred in recordings#index: Real HTTP connections are disabled.` error, even after following the suggested `stub_request`. I have the stub request in my spec's background. Is what I am trying to accomplish actually possible with WebMock alone? – onebree Dec 29 '14 at 15:54
  • Yes. The reason the error is being raised is because the stub_request does not match the request *exactly* or it is after the request is being made. Looking at your code, it seems that the stub_request location is good. You are mocking localhost but requesting www.example.com. These have to match. – B Seven Dec 29 '14 at 17:55
  • I am on the Android app, and will update my question when i reach a computer. At the moment, i changed the block in `spec_helper.rb` and my spec to contain `recorder.example.com:8080/recorder/index`. .... I feel like I'm just missing some integral understanding of stubs and mocks, even after reading a dozen articles and following your advice on this question. – onebree Dec 29 '14 at 21:35
  • Funny, I was thinking the same thing. I suggest doing a tutorial on WebMock so you can understand how it works outside of your app. Have you seen this one? http://robots.thoughtbot.com/how-to-stub-external-services-in-tests – B Seven Dec 29 '14 at 21:40
  • Also, you may find this book helpful: https://pragprog.com/book/nrtest2/rails-4-test-prescriptions – B Seven Dec 29 '14 at 21:41
  • Thought bot actually helped me understand a lot... I modeled my spec after theirs, since it was the only tutorial i found online that showed WebMock inside a spec file. I'm going to reread your suggestions today and tomorrow. I gladly appreciate your help. – onebree Dec 29 '14 at 21:45
  • I'm going to mark this post as the answer. Although the post itself didn't help, the comments are the best part – onebree Dec 29 '14 at 21:45
  • OK, thanks. I suggest going through some RSpec tutorials so you can understand how testing works outside of your app. For example, here is one that will hopefully make sense to you: http://railscasts.com/episodes/275-how-i-test. I highly recommend RailsCasts as a source of info. – B Seven Dec 29 '14 at 22:46
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/67917/discussion-between-onebree-and-b-seven). – onebree Dec 30 '14 at 13:31