Fixrb

matchi

Framework-agnostic matchers for Ruby.

A matcher is built with an expected value, then asked whether an actual value matches. It answers true or false, and leaves negation, messages and reports to your test framework.

Version
5.0.0
Ruby
3.3 or later
Dependencies
None
License
MIT
Used by
Fix 1.0

Install

Add gem "matchi" to your Gemfile, or run gem install matchi.

Usage

The block given to match? yields the actual value once, and to_s completes the sentence “expected … to …”.

require "matchi"

matcher = Matchi::Eq.new(42)
matcher.match? { 42 } # => true
matcher.match? { 41 } # => false
matcher.to_s          # => "eq 42"

Matchers

Be
Identity, with equal?.
Eq
Equivalence, with eql?, between values of the same class.
BeAKindOf
A kind of a module, given directly or by name.
BeAnInstanceOf
An instance of a class, given directly or by name.
BeWithin
An Integer, Float, Rational or BigDecimal within a delta of the expected number.
Match
A string or a symbol that matches a Regexp.
Satisfy
A value for which a block answers true.
Predicate
A value whose predicate answers true: :be_empty calls empty?.
RaiseException
A block that raises an exception of a given class, optionally with a given message.
Change
A block that changes what a public method returns, optionally by, by_at_least, by_at_most, to, or from and to.

The expected value decides

When a matcher compares the expected value with the actual value, the expected value receives the call: Matchi::Eq.new(a) evaluates a.eql?(b), never b.eql?(a). An actual value that lies about its own equality cannot pass:

require "matchi"

actual = "bar"

def actual.eql?(*)
  true
end

actual.eql?("foo") # => true

matcher = Matchi::Eq.new("foo")
matcher.match? { actual } # => false

Three cases rely on values produced by the object under test, and so on its honesty: Predicate calls one of its methods, the by refinements of Change compare and subtract the values it returns, and the message filter of RaiseException reads the message of the exception raised. The README says which matchers give which guarantee.

The reasons for this rule are in The Fundamental Distinction Between Expected and Actual Values in Testing.

With your test framework

Any framework can use a matcher in a few lines. With Minitest:

require "matchi"
require "minitest/autorun"

class TestAnswer < Minitest::Test
  def assert_matches(matcher, actual)
    message = "expected #{actual.inspect} to #{matcher}"
    assert matcher.match? { actual }, message
  end

  def test_answer
    assert_matches Matchi::BeAKindOf.new(Integer), 42
    assert_matches Matchi::BeWithin.new(0.01).of(0.3), 0.1 + 0.2
  end
end

Documentation