Matchi

Version Yard documentation CI RuboCop License

Collection of expectation matchers for Rubyists 🤹

A Rubyist juggling between Matchi letters

Project goals

  • Adding matchers should be as simple as possible.
  • Being framework agnostic and easy to integrate.
  • Avoid false positives/negatives due to malicious actual values.

Installation

Add this line to your application's Gemfile:

gem "matchi"

And then execute:

bundle

Or install it yourself as:

gem install matchi

Overview

Matchi provides a collection of damn simple expectation matchers.

Usage

To make Matchi available:

require "matchi"

All examples here assume that this has been done.

Anatomy of a matcher

A Matchi matcher is an object that must respond to the matches? method with a block as argument, and return a boolean.

To facilitate the integration of the matchers in other tools, Matchi matchers may expose expected values via the expected method.

Built-in matchers

Here is the collection of useful generic matchers.

Equivalence matcher:

matcher = Matchi::Eq.new("foo")

matcher.expected           # => "foo"
matcher.matches? { "foo" } # => true

Identity matcher:

matcher = Matchi::Be.new(:foo)

matcher.expected          # => :foo
matcher.matches? { :foo } # => true

Comparisons matcher:

matcher = Matchi::BeWithin.new(8).of(37)

matcher.expected        # => 37
matcher.matches? { 42 } # => true

Regular expressions matcher:

matcher = Matchi::Match.new(/^foo$/)

matcher.expected           # => /^foo$/
matcher.matches? { "foo" } # => true

Expecting errors matcher:

matcher = Matchi::RaiseException.new(:NameError)

matcher.expected          # => "NameError"
matcher.matches? { Boom } # => true

Type/class matcher:

matcher = Matchi::BeAnInstanceOf.new(:String)

matcher.expected           # => "String"
matcher.matches? { "foo" } # => true

Predicate matcher:

matcher = Matchi::Predicate.new(:be_empty)

matcher.expected        # => [:empty?, [], {}, nil]
matcher.matches? { [] } # => true

matcher = Matchi::Predicate.new(:have_key, :foo)

matcher.expected                 # => [:has_key?, [:foo], {}, nil]
matcher.matches? { { foo: 42 } } # => true

Change matcher:

object = []
matcher = Matchi::Change.new(object, :length).by(1)

matcher.expected                 # => 1
matcher.matches? { object << 1 } # => true

object = []
matcher = Matchi::Change.new(object, :length).by_at_least(1)

matcher.expected                 # => 1
matcher.matches? { object << 1 } # => true

object = []
matcher = Matchi::Change.new(object, :length).by_at_most(1)

matcher.expected                 # => 1
matcher.matches? { object << 1 } # => true

object = "foo"
matcher = Matchi::Change.new(object, :to_s).from("foo").to("FOO")

matcher.expected                    # => "FOO"
matcher.matches? { object.upcase! } # => true

object = "foo"
matcher = Matchi::Change.new(object, :to_s).to("FOO")

matcher.expected                    # => "FOO"
matcher.matches? { object.upcase! } # => true

Satisfy matcher:

matcher = Matchi::Satisfy.new { |value| value == 42 }

matcher.expected        # => #<Proc:0x00007fbaafc65540>
matcher.matches? { 42 } # => true

Custom matchers

Custom matchers can easily be added to express more specific expectations.

A Be the answer matcher:

module Matchi
  class BeTheAnswer
    def expected
      42
    end

    def matches?
      expected.equal?(yield)
    end
  end
end

matcher = Matchi::BeTheAnswer.new

matcher.expected        # => 42
matcher.matches? { 42 } # => true

A Be prime matcher:

require "prime"

module Matchi
  class BePrime
    def matches?
      Prime.prime?(yield)
    end
  end
end

matcher = Matchi::BePrime.new

matcher.matches? { 42 } # => false

A Start with matcher:

module Matchi
  class StartWith
    attr_reader :expected

    def initialize(expected)
      @expected = expected
    end

    def matches?
      Regexp.new(/\A#{expected}/).match?(yield)
    end
  end
end

matcher = Matchi::StartWith.new("foo")

matcher.expected              # => "foo"
matcher.matches? { "foobar" } # => true

Contact

Versioning

Matchi follows Semantic Versioning 2.0.

License

The gem is available as open source under the terms of the MIT License.


This project is sponsored by:
Sashite