Search code examples
rubyrspecnokogiriopen-uri

How do I test this particular method?


I have the following method that is responsible for requesting a URL and returning it's Nokogiri::HTML document. This method checks if a proxy is defined and if it does, it will call OpenURI's open with or without the proxy options.

Implementation

require 'open-uri'
require 'nokogiri'

class MyClass 
  attr_accessor :proxy

  # ....

  def self.page_content(url)
    if MyClass.proxy
      proxy_uri = URI.parse(MyClass.proxy)
      Nokogiri::HTML(open(url, :proxy => proxy_uri)) # open provided by OpenURI
    else
      Nokogiri::HTML(open(url)) # open provided by OpenURI
    end
  end
end

I have no idea how I should write tests that prove the following:

  1. When a proxy is defined the request OpenURI makes actually uses the proxy info
  2. When a proxy isn't defined, a regular non-proxy connection is made

Here's what I came up with as a start for the tests.

describe MyClass, :vcr do

  describe '.proxy' do
    it { should respond_to(:proxy) }
  end

  describe '.page_content' do
    let(:url) { "https://google.com/" }
    let(:page_content) { subject.page_content(url) }

    it 'returns a Nokogiri::HTML::Document' do
      page_content.should be_a(Nokogiri::HTML::Document)
    end

    # How do i test this method actually uses a proxy when it's set vs not set?
    context 'when using a proxy' do
      # ???
      xit 'should set open-uri proxy properties' do
      end
    end

    context 'when not using a proxy' do
      # ???
      xit 'should not set open-uri proxy properties' do
      end
    end

  end

end

Solution

  • First of all, you need to arrange for the proxy method to return a proxy in one test case and not in the other. If there is a "setter" method for proxy, you can use that, otherwise you can stub the proxy method.

    Then, at a minimum, you want to set an expectation on open that it will be called with or without the :proxy option, depending on which test it is. Beyond that, you have the choice of whether to stub and set expectations for the various other calls involved in the method, including URI.parse and Nokogiri::HTML.

    See https://github.com/rspec/rspec-mocks for information on establishing your test doubles and setting expectations. Note in particular the and_call_original option if you want to use a partial stubbing approach.

    Update: Here's some code to get you started. This works for the non-proxy method. I've left the proxy case for you. Note also that this uses the "partial stubbing" approach, where you still end up calling the external gems.

    require 'spec_helper'
    
    describe MyClass do
    
      describe '.proxy' do         # NOTE: This test succeeds because of attr_accessor, but you're calling a MyClass.proxy (a class method) within your page_content method
        it { should respond_to(:proxy) }
      end
    
      describe '.page_content' do
        let(:url) { "https://google.com/" }
        let(:page_content) { MyClass.page_content(url) } # NOTE: Changed to invoke class method
    
        context 'when not using a proxy' do
    
          before {allow(MyClass).to receive(:proxy).and_return(false)} # Stubbed for no-proxy case
    
          it 'returns a Nokogiri::HTML::Document' do
            page_content.should be_a(Nokogiri::HTML::Document)
          end
    
          it 'should not set open-uri proxy properties' do
            expect(MyClass).to receive(:open).with(url).and_call_original  # Stubbing open is tricky, see note afterwards
            page_content
          end
        end
            # How do i test this method actually uses a proxy when it's set vs not set?
        context 'when using a proxy' do
          # ???
          xit 'should set open-uri proxy properties' do
          end
        end
    
      end
    
    end
    

    Stubbing of open is tricky. See How to rspec mock open-uri? for an explanation.