Having trouble with WebMock, not stubbing correctly
Asked Answered
C

2

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.

Carrizales answered 26/12, 2014 at 21:58 Comment(0)
C
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

Carrizales answered 31/12, 2014 at 14:55 Comment(0)
T
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").
Tattletale answered 27/12, 2014 at 5:46 Comment(13)
Thank you. The test.rb file has these configurations commented out. Do i need to define them in that file before stubbing?Carrizales
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.Tattletale
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?Tattletale
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.Tattletale
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?Carrizales
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.Tattletale
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.Carrizales
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? robots.thoughtbot.com/how-to-stub-external-services-in-testsTattletale
Also, you may find this book helpful: pragprog.com/book/nrtest2/rails-4-test-prescriptionsTattletale
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.Carrizales
I'm going to mark this post as the answer. Although the post itself didn't help, the comments are the best partCarrizales
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: railscasts.com/episodes/275-how-i-test. I highly recommend RailsCasts as a source of info.Tattletale
Let us continue this discussion in chat.Carrizales

© 2022 - 2024 — McMap. All rights reserved.