Back to Repositories

Testing HTTP Request Configuration Implementation in lostisland/faraday

This test suite thoroughly validates the Faraday::Request class functionality, focusing on request building, configuration, and header management. The tests ensure proper handling of HTTP methods, URL construction, and request options in the Faraday HTTP client library.

Test Coverage Overview

The test suite provides comprehensive coverage of Faraday’s request handling capabilities:

  • HTTP method configuration and validation
  • URL construction with various parameter formats
  • Header management and manipulation
  • Request body handling
  • Global and per-request configuration options

Implementation Analysis

The testing approach uses RSpec’s context-based structure to organize different request scenarios. It leverages let blocks for setup and subject for consistent request building. The implementation demonstrates proper isolation of test cases and thorough validation of request components.

Technical Details

  • Testing Framework: RSpec
  • Subject: Faraday::Request class
  • Test Environment: Isolated request building
  • Key Components: HTTP methods, URL parsing, headers, request options
  • Serialization testing with Marshal

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Proper context isolation
  • Consistent setup patterns
  • Comprehensive edge case coverage
  • Clear test organization
  • Effective use of RSpec expectations
  • Thorough validation of complex objects

lostisland/faraday

spec/faraday/request_spec.rb

            
# frozen_string_literal: true

RSpec.describe Faraday::Request do
  let(:conn) do
    Faraday.new(url: 'http://httpbingo.org/api',
                headers: { 'Mime-Version' => '1.0' },
                request: { oauth: { consumer_key: 'anonymous' } })
  end
  let(:http_method) { :get }
  let(:block) { nil }

  subject { conn.build_request(http_method, &block) }

  context 'when nothing particular is configured' do
    it { expect(subject.http_method).to eq(:get) }
    it { expect(subject.to_env(conn).ssl.verify).to be_falsey }
    it { expect(subject.to_env(conn).ssl.verify_hostname).to be_falsey }
  end

  context 'when HTTP method is post' do
    let(:http_method) { :post }

    it { expect(subject.http_method).to eq(:post) }
  end

  context 'when setting the url on setup with a URI' do
    let(:block) { proc { |req| req.url URI.parse('foo.json?a=1') } }

    it { expect(subject.path).to eq(URI.parse('foo.json')) }
    it { expect(subject.params).to eq('a' => '1') }
    it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') }
  end

  context 'when setting the url on setup with a string path and params' do
    let(:block) { proc { |req| req.url 'foo.json', 'a' => 1 } }

    it { expect(subject.path).to eq('foo.json') }
    it { expect(subject.params).to eq('a' => 1) }
    it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') }
  end

  context 'when setting the url on setup with a path including params' do
    let(:block) { proc { |req| req.url 'foo.json?b=2&a=1#qqq' } }

    it { expect(subject.path).to eq('foo.json') }
    it { expect(subject.params).to eq('a' => '1', 'b' => '2') }
    it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1&b=2') }
  end

  context 'when setting a header on setup with []= syntax' do
    let(:block) { proc { |req| req['Server'] = 'Faraday' } }
    let(:headers) { subject.to_env(conn).request_headers }

    it { expect(subject.headers['Server']).to eq('Faraday') }
    it { expect(headers['mime-version']).to eq('1.0') }
    it { expect(headers['server']).to eq('Faraday') }
  end

  context 'when setting the body on setup' do
    let(:block) { proc { |req| req.body = 'hi' } }

    it { expect(subject.body).to eq('hi') }
    it { expect(subject.to_env(conn).body).to eq('hi') }
  end

  context 'with global request options set' do
    let(:env_request) { subject.to_env(conn).request }

    before do
      conn.options.timeout = 3
      conn.options.open_timeout = 5
      conn.ssl.verify = false
      conn.proxy = 'http://proxy.com'
    end

    it { expect(subject.options.timeout).to eq(3) }
    it { expect(subject.options.open_timeout).to eq(5) }
    it { expect(env_request.timeout).to eq(3) }
    it { expect(env_request.open_timeout).to eq(5) }

    context 'and per-request options set' do
      let(:block) do
        proc do |req|
          req.options.timeout = 10
          req.options.boundary = 'boo'
          req.options.oauth[:consumer_secret] = 'xyz'
          req.options.context = {
            foo: 'foo',
            bar: 'bar'
          }
        end
      end

      it { expect(subject.options.timeout).to eq(10) }
      it { expect(subject.options.open_timeout).to eq(5) }
      it { expect(env_request.timeout).to eq(10) }
      it { expect(env_request.open_timeout).to eq(5) }
      it { expect(env_request.boundary).to eq('boo') }
      it { expect(env_request.context).to eq(foo: 'foo', bar: 'bar') }
      it do
        oauth_expected = { consumer_secret: 'xyz', consumer_key: 'anonymous' }
        expect(env_request.oauth).to eq(oauth_expected)
      end
    end
  end

  it 'supports marshal serialization' do
    expect(Marshal.load(Marshal.dump(subject))).to eq(subject)
  end
end