rspec-openapi

repository·master·Indexed 19 days ago

https://github.com/exoego/rspec-openapi

A Ruby gem that automates the generation of OpenAPI schemas by inspecting existing RSpec request specs. It allows for schema generation without a custom DSL, preserves manual schema edits, and supports OpenAPI versions 3.0, 3.1, and 3.2. It includes features for handling enums, dynamic keys via additionalProperties, multiple example modes, and experimental support for Minitest.

Tokens
7.4K
Snippets
26
Records
34
Agent score
67%

What's inside rspec-openapi

  1. What is rspec-openapi?

    master

    rspec-openapi is a tool used to generate OpenAPI schemas directly from RSpec request specs.

    Key advantages include:

    • No special DSL required: Unlike other gems that require a custom domain-specific language, rspec-openapi works with your existing request specs without modification.
    • Preserves manual changes: When merging automated changes from specs into your OpenAPI files, it attempts to keep manual modifications that cannot be fully automated from the specs.
  2. Use $ref to minimize schema duplication

    master

    rspec-openapi v0.7.0+ supports the $ref mechanism. You can manually replace duplicated schema structures in your doc/openapi.yaml with $ref pointers (e.g., $ref: "#/components/schemas/User"). When you re-run the generator, it will automatically detect these references and populate the #/components/schemas section with the corresponding schema definitions.

    paths:
      "/users":
        get:
          responses:
            '200':
              content:
                application/json:
                  schema:
                    $ref: "#/components/schemas/User"
    components:
      schemas:
        User:
          type: object
          properties:
            id: { type: string }
            name: { type: string }
  3. How `openapi:` metadata inheritance works

    master

    The openapi: metadata is inherited from surrounding describe/context groups down to individual examples.

    • Inheritance: Inner levels inherit keys from outer levels.
    • Overriding: Inner levels override outer keys on a per-key basis.
    • Merge Logic: The merge happens at the top level of the openapi: hash. Scalar keys (like summary) follow a last-wins strategy.
    • Structured Keys: For structured keys (like tags, security, or enum), a nested level that re-declares the key replaces the inherited value entirely rather than performing a deep merge.
    describe 'GET /tables', openapi: { summary: 'Get a list of tables', tags: %w[Table] } do
      context 'with pagination', openapi: { example_mode: :multiple } do
        # This example inherits summary: 'Get a list of tables' and tags: ['Table']
        # and adds example_mode: :multiple.
        it { get '/tables', params: { page: 1 } }
      end
    end
  4. Use experimental Minitest support

    master

    While primarily designed for RSpec, rspec-openapi provides experimental support for minitest. To use it, call the openapi! method within your test class. This works for classes inheriting from ActionDispatch::IntegrationTest or those using Rack::Test directly.

    Limitations:

    • Custom per-test case metadata is not currently supported.
    • Custom description_builder is not currently supported.
    class TablesTest < ActionDispatch::IntegrationTest
      openapi!
    
      test "GET /index returns a list of tables" do
        get '/tables', params: { page: '1', per: '10' }, headers: { authorization: 'k0kubun' }
        assert_response :success
      end
    
      test "GET /index does not return tables if unauthorized" do
        get '/tables'
        assert_response :unauthorized
      end
    end
  5. Exclude specs from OpenAPI generation

    master

    To prevent specific RSpec examples or groups from being included in the generated OpenAPI schema, set openapi: false in the RSpec metadata.

    # Exclude an entire describe block
    RSpec.describe '/resources', type: :request, openapi: false do
      # ...
    end
    
    # Exclude a single test case
    rit 'returns a resource', openapi: false do
      # ...
    end
  6. Generate OpenAPI schema from RSpec request specs

    master

    To generate an OpenAPI schema file from your RSpec request specs, run your RSpec suite with the OPENAPI=1 environment variable. By default, this will generate a doc/openapi.yaml file containing the paths, parameters, and responses derived from your specs.

    $ OPENAPI=1 bundle exec rspec
  7. Configure request body example modes

    master

    When building schemas, you can control how examples are emitted using the example_mode on a record. This affects both request bodies and response content:

    • :none: Opts out of recording examples. The description is treated as provisional and stashed under :_fallback_description until a documented test sets a permanent one.
    • :single (default): Emits a single example object. It may also include metadata like _example_key and _example_summary if enabled.
    • :multiple: Emits an examples object where each example is keyed by record.example_key, allowing for multiple named examples in the schema.
  8. Identify sequential (streaming) media types

    master

    The library recognizes specific media types as sequential (streaming), meaning the body is treated as a sequence of items rather than a single document. For these types, the raw body is kept unparsed and split per item using the StreamParser.

    Supported sequential media types:

    • application/jsonl
    • application/x-ndjson
    • application/json-seq
    • text/event-stream
  9. Configure example_mode for request and response examples

    master

    The example_mode metadata option controls whether RSpec examples provide a single example or multiple examples for the request and response in the generated OpenAPI schema.

    Valid modes are :single, :multiple, or :none. You can specify a single mode for both sides, or use a Hash to specify different modes for the request and response.

    Deprecation Warning: Using example_mode: :multiple is currently a shorthand for { request: :single, response: :multiple }. A future major version will change this shorthand to { request: :multiple, response: :multiple }. To avoid ambiguity, it is recommended to use the explicit Hash form.

    # Shorthand (Deprecated behavior: request: :single, response: :multiple)
    example_mode: :multiple
    
    # Explicit Hash (Recommended)
    example_mode: { request: :single, response: :multiple }
    
    # Both sides single
    example_mode: :single
  10. Configure additional_properties for requests and responses

    master

    You can define additional_properties for the OpenAPI schema. The extractor supports several levels of specificity to allow fine-grained control over how extra properties are handled in the request and response:

    • additional_properties: The base configuration applied to both request and response.
    • request_additional_properties: Overrides the base for the request.
    • response_additional_properties: Overrides the base for the response.
    • hybrid_additional_properties: A base for hybrid configurations.
    • request_hybrid_additional_properties: Overrides the hybrid base for the request.
    • response_hybrid_additional_properties: Overrides the hybrid base for the response.