All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
This release is a complete architectural redesign. See the Migration Guide for detailed upgrade instructions.
Core changes:
spec_forge/specs/ → spec_forge/blueprints/
spec_forge new spec → spec_forge new blueprint
variables.name → {{ name }} template syntaxConfiguration:
config.headers, config.query (use explicit inheritance instead)config.specs → config.rspec
config.on_debug = proc → config.on_debug { block }
Step-based workflows: Tests execute as sequential steps with explicit data flow between them. See Writing Tests.
- name: "Create user"
request:
url: /users
http_verb: POST
json:
email: "{{ faker.internet.email }}"
expect:
- status: 201
store:
user_id: "{{ response.body.id }}"
- name: "Fetch created user"
request:
url: "/users/{{ user_id }}"
expect:
- status: 200
Template variable system: New {{ }} syntax for dynamic values—supports variables, Faker, factories, and environment variables. See Dynamic Features.
JSON validation modes: Three modes for response validation—shape: for type checking with nullable/optional flags, content: for value matching, and schema: for explicit structure control. See Validating Responses.
Configuration inheritance: The shared: wrapper applies request configuration and hooks to nested steps, making auth flows and grouped operations cleaner.
Lifecycle hooks: Register callbacks for before/after events at forge, blueprint, and step levels. See Callbacks.
Tag-based filtering: Organize steps with tags and run subsets via --tags and --skip-tags CLI options. See Running Tests.
Improved output display: Verbosity levels (--verbose, --debug, --trace) with colorized terminal output, detailed failure context, and YAML line number tracking in error messages.
File includes: Extract common workflows into separate files and inject them with include:.
request: keyexpectations: [{ expect: ... }] to expect: [...]
forge_helper.rb via config.global_variables instead of YAMLContext::Global, Context::Store, Context::Variables)shared: inheritance instead)Full documentation | Migration Guide
base_path can now be used as an alias for base_url in spec definitionsImproved debug configuration: New config.on_debug { } block syntax for cleaner configuration
# New cleaner syntax (recommended)
config.on_debug { binding.pry }
# Old syntax still works with deprecation warning
config.on_debug = -> { binding.pry }
/users) now properly append to base URLs instead of replacing the entire path
http://api.example.com/v1 with path /users would incorrectly resolve to http://api.example.com/users
http://api.example.com/v1/users
https://api.example.com/users
config.on_debug = proc syntax - use config.on_debug { } block syntax insteadThe Big Picture: SpecForge now generates OpenAPI documentation from your tests automatically!
Primary Documentation Workflow: New docs command (now the default!) generates OpenAPI specs from test execution
--fresh flag for forced regeneration--format
--output option--skip-validation for faster iterationsLive Documentation Server: spec_forge serve command for immediate feedback
--ui redoc)--port (defaults to 8080)Flexible Configuration System:
config/components/, config/paths/, etc.$ref supportHTTP Header Testing: Comprehensive header validation
headers:
Content-Type: "application/json"
X-Request-ID: /^[0-9a-f-]{36}$/
Cache-Control:
matcher.and:
- matcher.include: "max-age="
- matcher.include: "private"
Flexible Store System: Store anything, access everything
store.id.attribute syntaxDocumentation Control: Fine-grained control over what gets documented
documentation: true/false attribute for specs and expectationsYAML-Driven Normalizers: Configuration over code
lib/spec_forge/normalizers/*.yml
reference: system for reusable components*) for catch-all schemasEnhanced CLI Experience:
init command with --skip-openapi and --skip-factories flagsDeveloper Utilities:
Array#to_merged_h for cleaner hash merging.normalize!(input, using:) API across normalizersRunner.prepare) from executionNew Default Behavior: spec_forge without arguments now shows help instead of running tests
spec_forge docs for documentation or spec_forge run for test-only executionStreamlined Commands:
Normalizer Architecture: YAML-based instead of class-heavy approach
_shared.yml
Test Execution Pipeline:
HTTP & Store Improvements:
Migration Notes:
spec_forge - now shows help instead of running testsspec_forge docs for documentation generation or spec_forge run for testingSpecForge.context global accessor for accessing test contextContext class with modular components:
Context::Global for file-level shared variablesContext::Variables for managing variables with overlay supportContext::Store for storing the results of the testsAdded support for defining and referencing global variables
global:
variables:
api_version: "v2"
environment: "test"
index_user:
path: "/{api_version}/users"
query:
api_version: "global.variables.api_version"
matcher.and for combining multiple matchers
email:
matcher.and:
- kind_of.string
- /@/
- matcher.end_with: ".com"
have_size for checking an object’s size via matcher.have_size
Loader class for improved spec file processingFilter class for more flexible test filteringAdded support for defining and referencing callbacks
# Configuration level
SpecForge.configure do |config|
config.register_callback("callback_name") { |context| }
# These are aliases
# config.define_callback("callback_name") { |context| }
# config.callback("callback_name") { |context| }
end
# Module level (no aliases)
SpecForge.register_callback("callback_name") { |context| }
Once defined, callbacks can be referenced in spec files via the global context
global:
callbacks:
- before: callback_name
after: cleanup_database_state
Added support for storing and retrieving test data via the store_as directive and store attribute
create_user:
path: "/users"
method: "post"
expectations:
- variables:
name: "John"
email: "[email protected]"
store_as: "created_user"
expect:
status: 200
show_user:
path: "/users/:id"
query:
id: store.created_user.response.id
- expect:
status: 200
UndefinedMatcherError for clearer error messaging when invalid matchers are usedquery, body, and variables attributesSpecForge.forge to SpecForge.forge_path
http_method to http_verb (http_method is now an alias)Attribute#resolve to #resolved (memoized version)Attribute#resolve_value to #resolve (immediate resolution)Attribute#resolve_as_matcher for resolving attributes into RSpec matchersRunner to properly initialize and manage context between testsSpecForge::Error
matcher.include) would cause an errorinclude matcher for flexibilityAttribute::Matcher to accept either matcher or matchers namespacecontain_exactly to eq([])
include to eq({})
forge_and description from “matches all of:” to “match all:”Configuration.overlay_options
json
expected_json_class variable.size attribute
variables:
users:
factory.user:
size: 10 # Creates 10 user records
create_list (default)build_listbuild_stubbed_listattributes_for_listbuild_paircreate_pairConstraint to use include for testing Hashes and contains_exactly for testing ArraysMatcher and Faker attributeseverythingrb to 0.2run command’s CLI documentationflake.nix to use Ruby 3.4Core Infrastructure
CLI
init)new)run)Attributes
Chainable)Parameterized)factories.
faker.
variables.
1, Some text, false, etc.be, kind_of, matcher
transform.
HTTP
{id} or :id syntax