Back to Repositories

Testing Request Instrumentation Implementation in Faraday

This test suite validates the instrumentation functionality in Faraday’s request handling, focusing on custom and default instrumenters for monitoring HTTP requests. The tests ensure proper event capturing and configuration flexibility.

Test Coverage Overview

The test suite provides comprehensive coverage of Faraday’s request instrumentation capabilities.

Key areas tested include:
  • Default instrumentation behavior and naming
  • Integration with ActiveSupport::Notifications
  • Custom instrumenter implementation
  • Request environment capture and validation

Implementation Analysis

The testing approach utilizes RSpec’s context-based structure to organize different instrumentation scenarios. The implementation leverages a FakeInstrumenter class to mock the instrumentation behavior, allowing for precise verification of event capturing and naming conventions.

Notable patterns include:
  • Shared connection setup using Faraday’s builder pattern
  • Isolated instrumenter state management
  • Contextual test organization for different configurations

Technical Details

Testing tools and setup:
  • RSpec for test framework
  • Faraday’s test adapter for HTTP stubbing
  • Custom FakeInstrumenter class for instrumentation mocking
  • Configuration options handling through Options class
  • ActiveSupport::Notifications integration testing

Best Practices Demonstrated

The test suite exemplifies several testing best practices for Ruby libraries.

Notable practices include:
  • Isolated test environments using let blocks
  • Clear separation of concerns in test organization
  • Comprehensive edge case coverage
  • Proper error handling validation
  • Clean and maintainable test structure

lostisland/faraday

spec/faraday/request/instrumentation_spec.rb

            
# frozen_string_literal: true

RSpec.describe Faraday::Request::Instrumentation do
  class FakeInstrumenter
    attr_reader :instrumentations

    def initialize
      @instrumentations = []
    end

    def instrument(name, env)
      @instrumentations << [name, env]
      yield
    end
  end

  let(:config) { {} }
  let(:options) { Faraday::Request::Instrumentation::Options.from config }
  let(:instrumenter) { FakeInstrumenter.new }
  let(:conn) do
    Faraday.new do |f|
      f.request :instrumentation, config.merge(instrumenter: instrumenter)
      f.adapter :test do |stub|
        stub.get '/' do
          [200, {}, 'ok']
        end
      end
    end
  end

  it { expect(options.name).to eq('request.faraday') }
  it 'defaults to ActiveSupport::Notifications' do
    res = options.instrumenter
  rescue NameError => e
    expect(e.to_s).to match('ActiveSupport')
  else
    expect(res).to eq(ActiveSupport::Notifications)
  end

  it 'instruments with default name' do
    expect(instrumenter.instrumentations.size).to eq(0)

    res = conn.get '/'
    expect(res.body).to eq('ok')
    expect(instrumenter.instrumentations.size).to eq(1)

    name, env = instrumenter.instrumentations.first
    expect(name).to eq('request.faraday')
    expect(env[:url].path).to eq('/')
  end

  context 'with custom name' do
    let(:config) { { name: 'custom' } }

    it { expect(options.name).to eq('custom') }
    it 'instruments with custom name' do
      expect(instrumenter.instrumentations.size).to eq(0)

      res = conn.get '/'
      expect(res.body).to eq('ok')
      expect(instrumenter.instrumentations.size).to eq(1)

      name, env = instrumenter.instrumentations.first
      expect(name).to eq('custom')
      expect(env[:url].path).to eq('/')
    end
  end

  context 'with custom instrumenter' do
    let(:config) { { instrumenter: :custom } }

    it { expect(options.instrumenter).to eq(:custom) }
  end
end