Skip to main content

dynamic_chaining_matchers.md

🛠️ Advanced Chaining and Dynamic Parameters

Custom matchers can expose richly chainable interfaces to express complex assertions cleanly. By using chain, match, and dynamic arguments, you can tailor matchers for different contexts without repetition. This approach is ideal for verifying flexible data structures or multi-step transformations while keeping your specs concise.

# spec/support/matchers/validate_json_schema.rb
RSpec::Matchers.define :match_json_schema do |schema_name|
match do |actual_json|
schema = JSON::SchemaLoader.load("spec/schemas/#{schema_name}.json")
JSON::Validator.validate!(schema, actual_json)
true
rescue JSON::Schema::ValidationError
false
end

chain :with_error_message do |expected_message|
@expected_message = expected_message
end

match_when_negated do |actual_json|
JSON::Validator.validate!(JSON::SchemaLoader.load("spec/schemas/#{schema_name}.json"), actual_json)
false
rescue JSON::Schema::ValidationError => e
e.message.include?(@expected_message)
end

failure_message do |actual_json|
"expected JSON to match '#{schema_name}' schema but it failed: #{JSON::Validator.fully_validate(JSON::SchemaLoader.load("spec/schemas/#{schema_name}.json"), actual_json).join(", ")}"
end
end

# Usage in a spec:
expect(response.body).to match_json_schema(:user_profile).with_error_message("required property 'id'")