Back to Repositories

Testing Hash Parameter Builder Implementation in ruby-grape/grape

This test suite examines the Hash parameter builder extension in Grape, focusing on parameter handling and type conversion functionality. The tests verify proper parameter processing, key symbolization, and route parameter handling in both endpoint and API contexts.

Test Coverage Overview

The test suite provides comprehensive coverage of the Hash parameter builder functionality in Grape.

Key areas tested include:
  • Parameter type verification as Hash
  • Key symbolization in nested parameter structures
  • Route parameter handling and precedence
  • Integration with both endpoint and API contexts

Implementation Analysis

The testing approach utilizes RSpec’s behavior-driven development patterns with focused test cases for parameter handling.

Technical implementation includes:
  • Dynamic class creation for API testing
  • Nested parameter structure validation
  • Route parameter conflict resolution testing
  • Response status and body verification

Technical Details

Testing infrastructure includes:
  • RSpec testing framework
  • Grape API framework
  • Hash::ParamBuilder extension
  • Mock HTTP request handling
  • Response status and body assertions

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolated test contexts using subject blocks
  • Clear test case organization and naming
  • Comprehensive edge case coverage
  • Proper setup and teardown patterns
  • Explicit expectation assertions

ruby-grape/grape

spec/grape/extensions/param_builders/hash_spec.rb

            
# frozen_string_literal: true

describe Grape::Extensions::Hash::ParamBuilder do
  subject { Class.new(Grape::API) }

  def app
    subject
  end

  describe 'in an endpoint' do
    describe '#params' do
      before do
        subject.params do
          build_with Grape::Extensions::Hash::ParamBuilder # rubocop:disable RSpec/DescribedClass
        end

        subject.get do
          params.class
        end
      end

      it 'is of type Hash' do
        get '/'
        expect(last_response.status).to eq(200)
        expect(last_response.body).to eq('Hash')
      end
    end
  end

  describe 'in an api' do
    before do
      subject.include Grape::Extensions::Hash::ParamBuilder # rubocop:disable RSpec/DescribedClass
    end

    describe '#params' do
      before do
        subject.get do
          params.class
        end
      end

      it 'is Hash' do
        get '/'
        expect(last_response.status).to eq(200)
        expect(last_response.body).to eq('Hash')
      end
    end

    it 'symbolizes params keys' do
      subject.params do
        optional :a, type: Hash do
          optional :b, type: Hash do
            optional :c, type: String
          end
          optional :d, type: Array
        end
      end

      subject.get '/foo' do
        [params[:a][:b][:c], params[:a][:d]]
      end

      get '/foo', 'a' => { b: { c: 'bar' }, 'd' => ['foo'] }
      expect(last_response.status).to eq(200)
      expect(last_response.body).to eq('["bar", ["foo"]]')
    end

    it 'symbolizes the params' do
      subject.params do
        build_with Grape::Extensions::Hash::ParamBuilder # rubocop:disable RSpec/DescribedClass
        requires :a, type: String
      end

      subject.get '/foo' do
        [params[:a], params['a']]
      end

      get '/foo', a: 'bar'
      expect(last_response.status).to eq(200)
      expect(last_response.body).to eq('["bar", nil]')
    end

    it 'does not overwrite route_param with a regular param if they have same name' do
      subject.namespace :route_param do
        route_param :foo do
          get { params.to_json }
        end
      end

      get '/route_param/bar', foo: 'baz'
      expect(last_response.status).to eq(200)
      expect(last_response.body).to eq('{"foo":"bar"}')
    end

    it 'does not overwrite route_param with a defined regular param if they have same name' do
      subject.namespace :route_param do
        params do
          requires :foo, type: String
        end
        route_param :foo do
          get do
            params[:foo]
          end
        end
      end

      get '/route_param/bar', foo: 'baz'
      expect(last_response.status).to eq(200)
      expect(last_response.body).to eq('bar')
    end
  end
end