No release in over 3 years
A Ruby gem that generates OpenAPI 3.0 documentation from Minitest integration tests in Rails applications. Uses serializers as the single source of truth for response schemas.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

 Project Readme

OpenapiMinitest

Generate OpenAPI 3.1 documentation from your Minitest integration tests. No DSL, no magic - just one helper method.

Installation

Add to your Gemfile:

gem "openapi_minitest"

Then run:

bundle install

Quick Start

1. Configure (optional)

# config/initializers/openapi_minitest.rb (Rails)
# or test/support/openapi.rb

OpenapiMinitest.configure do |config|
  config.title = "My API"
  config.version = "1.0.0"
  config.description = "API documentation"
  config.output_path = "doc/openapi.yml"  # YAML by default
  config.servers = ["https://api.example.com"]
end

2. Define Schemas

OpenapiMinitest.define_schema :User, {
  type: :object,
  properties: {
    id: { type: :integer },
    email: { type: :string, format: :email },
    name: { type: :string }
  },
  required: %w[id email]
}

OpenapiMinitest.define_schema :UserList, {
  type: :object,
  properties: {
    data: { type: :array, items: { "$ref" => "#/components/schemas/User" } },
    meta: {
      type: :object,
      properties: {
        total: { type: :integer },
        page: { type: :integer }
      }
    }
  }
}

OpenapiMinitest.define_schema :Error, {
  type: :object,
  properties: {
    error: { type: :string },
    code: { type: :integer }
  }
}

3. Write Tests

Write normal Minitest tests. Call document_response after each request you want documented:

class UsersApiTest < ActionDispatch::IntegrationTest
  def test_returns_users
    create(:user, name: "John")
    create(:user, name: "Jane")

    get "/api/users", headers: auth_headers

    assert_response 200
    document_response schema: :UserList, description: "Returns all users"

    body = JSON.parse(response.body)
    assert_equal 2, body["data"].size
  end

  def test_filters_users_by_name
    create(:user, name: "John")
    create(:user, name: "Jane")

    get "/api/users", params: { q: "John" }, headers: auth_headers

    assert_response 200
    document_response schema: :UserList, description: "Filters users by name"

    body = JSON.parse(response.body)
    assert_equal 1, body["data"].size
  end

  def test_returns_single_user
    user = create(:user, name: "John")

    get "/api/users/#{user.id}", headers: auth_headers

    assert_response 200
    document_response schema: :User, description: "User found", tags: ["Users"]
  end

  def test_user_not_found
    get "/api/users/999999", headers: auth_headers

    assert_response 404
    document_response schema: :Error, description: "User not found"
  end

  def test_creates_user
    post "/api/users",
      params: { user: { name: "New User", email: "new@example.com" } },
      headers: auth_headers,
      as: :json

    assert_response 201
    document_response schema: :User, description: "User created", tags: ["Users"]
  end

  def test_create_user_validation_error
    post "/api/users",
      params: { user: { name: "" } },
      headers: auth_headers,
      as: :json

    assert_response 422
    document_response schema: :Error, description: "Validation failed"
  end

  private

  def auth_headers
    { "Authorization" => "Bearer #{generate_token}" }
  end
end

4. Generate Documentation

# Run tests with documentation generation
OPENAPI_GENERATE=true rails test test/integration/

# Or use the rake task
rails openapi:generate

5. Browse Documentation (optional)

Run the install generator to add an API docs page powered by Scalar:

rails generate openapi_minitest:install

This creates:

  • app/controllers/api_docs_controller.rb — serves the docs UI and the OpenAPI spec file
  • app/views/api_docs/index.html.erb — Scalar API reference page (titled with your Rails app name)
  • Routes: GET /api-docs and GET /openapi.yml

Visit /api-docs in your browser to explore your API documentation interactively.

API Reference

Configuration Options

OpenapiMinitest.configure do |config|
  config.title = "My API"              # API title
  config.version = "1.0.0"             # API version
  config.description = "Description"   # API description
  config.output_path = "doc/openapi.yml"  # Output file path (YAML by default)
  config.servers = [                   # Server URLs
    "https://api.example.com",
    { url: "https://staging.example.com", description: "Staging" }
  ]
  config.security_schemes = {          # Security schemes
    bearer: {
      type: :http,
      scheme: :bearer
    }
  }
  config.validate_schema = true        # Validate responses against schemas
  config.strict_validation = false     # Fail if response contains undocumented fields
end

document_response

Call after making a request to record it for documentation:

document_response(
  schema: :SchemaName,          # Schema reference (Symbol) or inline schema (Hash)
  summary: "Operation summary", # Defaults to test name
  description: "Response desc", # Description of this response
  tags: ["Tag1", "Tag2"],       # Tags for grouping
  operation_id: "getUsers",     # Unique operation ID
  deprecated: false,            # Mark endpoint as deprecated
  strict: nil                   # Override strict validation (true/false/nil for global config)
)

Schema Definition

# Reference to another schema
OpenapiMinitest.define_schema :UserList, {
  type: :object,
  properties: {
    data: { type: :array, items: { "$ref" => "#/components/schemas/User" } }
  }
}

# Inline schema in tests
document_response schema: {
  type: :object,
  properties: {
    status: { type: :string }
  }
}

# Nullable fields (OpenAPI 3.1 syntax - use type array instead of nullable: true)
OpenapiMinitest.define_schema :Article, {
  type: :object,
  properties: {
    id: { type: :integer },
    title: { type: :string },
    subtitle: { type: [:string, :null] },  # nullable string
    published_at: { type: [:string, :null], format: :datetime }
  }
}

Features

  • No DSL - Just one helper method, write normal Minitest tests
  • Schema validation - Optionally validate responses against schemas during tests
  • Strict validation - Fail tests when responses contain undocumented fields
  • Auto-detection - Automatically extracts path parameters, query params
  • Security handling - Authorization headers automatically use configured security schemes
  • Multiple examples - Each test becomes an example in the docs
  • Request bodies - Captures POST/PUT/PATCH request bodies as examples

Strict Validation

Enable strict validation to catch undocumented fields in API responses. This helps ensure your documentation stays in sync with your implementation.

When strict mode is enabled, the test will fail if the response contains any fields not defined in the schema:

Response does not match schema:
The property '#/' contains additional properties ["unexpected_field"] outside of the schema when none are allowed

Global Configuration

OpenapiMinitest.configure do |config|
  config.strict_validation = true  # All schemas strictly validated
end

Per-Call Override

# Force strict validation for this endpoint
document_response schema: :User, strict: true

# Disable strict validation for this endpoint (e.g., third-party responses)
document_response schema: :ExternalData, strict: false

# Use global config setting (default behavior)
document_response schema: :User

Strict validation works by automatically injecting additionalProperties: false into all object schemas during validation. This includes nested objects and objects within arrays. If a schema already defines additionalProperties, that value is preserved.

How It Works

  1. You write normal integration tests
  2. Call document_response after requests you want documented
  3. The gem captures:
    • Request method, path, parameters, headers
    • Response status, body
    • Schema reference or definition
  4. After tests run, generates OpenAPI 3.1 YAML

Path Parameter Detection

The gem automatically detects numeric IDs in paths and converts them:

/api/users/123           -> /api/users/{user_id}
/api/users/123/posts/456 -> /api/users/{user_id}/posts/{post_id}

Security / Authentication

When you configure security_schemes and your tests include an Authorization header, the gem automatically adds the security property to those operations:

OpenapiMinitest.configure do |config|
  config.security_schemes = {
    bearer: {
      type: :http,
      scheme: :bearer
    }
  }
end

Operations with Authorization headers will be generated with:

security:
  - bearer: []

Validator Compatibility

This gem generates OpenAPI 3.1.0 which supports type arrays for nullable fields:

type:
  - string
  - 'null'

Some validators may not fully support OpenAPI 3.1.0 yet. If you encounter validation errors about type arrays, ensure your validator supports OpenAPI 3.1.0.

Non-Rails Usage

Include the DSL module manually:

class MyApiTest < Minitest::Test
  include OpenapiMinitest::DSL

  # ... your tests
end

# Generate documentation after tests
Minitest.after_run do
  if ENV["OPENAPI_GENERATE"]
    OpenapiMinitest::OpenAPI::Generator.new.write
  end
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/k0va1/openapi_minitest.

License

MIT