├── .gitignore ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── lib ├── ramcrest.rb └── ramcrest │ ├── aint.rb │ ├── anything.rb │ ├── assertion.rb │ ├── base_enumerable_matcher.rb │ ├── comparable.rb │ ├── equal_to.rb │ ├── has_attribute.rb │ ├── has_size.rb │ ├── includes.rb │ ├── includes_exactly.rb │ ├── includes_in_any_order_exactly.rb │ ├── is.rb │ ├── matcher.rb │ ├── matcher_matcher.rb │ ├── minitest_assert.rb │ ├── rspec.rb │ └── such_that.rb ├── ramcrest.gemspec ├── spec └── rspec_integration_spec.rb └── test ├── aint_test.rb ├── anything_test.rb ├── assert_that_test.rb ├── comparable_test.rb ├── has_attribute_test.rb ├── has_size_test.rb ├── includes_exactly_test.rb ├── includes_in_any_order_exactly_test.rb ├── includes_test.rb ├── is_test.rb ├── matcher_matcher_test.rb ├── matcher_test.rb └── such_that_test.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | pkg/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.8.7 4 | - 1.9.2 5 | - 1.9.3 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | group :test do 3 | gem "minitest", ">= 3.2.0" 4 | gem "rake", ">= 0.9.2" 5 | gem "rspec", ">= 2.11.0" 6 | end 7 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | diff-lcs (1.1.3) 5 | minitest (3.2.0) 6 | rake (0.9.2.2) 7 | rspec (2.11.0) 8 | rspec-core (~> 2.11.0) 9 | rspec-expectations (~> 2.11.0) 10 | rspec-mocks (~> 2.11.0) 11 | rspec-core (2.11.0) 12 | rspec-expectations (2.11.1) 13 | diff-lcs (~> 1.1.3) 14 | rspec-mocks (2.11.1) 15 | 16 | PLATFORMS 17 | ruby 18 | 19 | DEPENDENCIES 20 | minitest (>= 3.2.0) 21 | rake (>= 0.9.2) 22 | rspec (>= 2.11.0) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ramcrest: a Ruby port of Hamcrest [![Build Status](https://secure.travis-ci.org/hamcrest/ramcrest.png?branch=master)](http://travis-ci.org/hamcrest/ramcrest) 2 | --------------------------------- 3 | 4 | Hamcrest is a powerful set of classes and functions that allow building 5 | up complicated matching expressions. The matchers provide useful descriptions 6 | of what they are trying to match, as well as a description of why a particular 7 | object is not matched (a mismatch description). 8 | 9 | The tests for the various matchers should provide great examples of usage because 10 | each matcher is actually tested using the Ramcrest framework. 11 | 12 | There are integrations into MiniTest via `assert_that(actual, matcher)` and 13 | into RSpec 2.11 via `expect(actual).that matcher`. 14 | 15 | How do I use this? 16 | ------------------ 17 | 18 | In your tests you need to 19 | 20 | ````ruby 21 | require 'ramcrest' 22 | ```` 23 | 24 | which will bring in all of the base Ramcrest matchers and give you a new 25 | `assert_that(subject, matcher)` assertion for your MiniTest tests. Once you 26 | have loaded the matchers you can now `include` them in your tests and start 27 | matching away! 28 | 29 | ````ruby 30 | describe My::Funky::Class do 31 | include Ramcrest::HasAttribute 32 | include Ramcrest::IncludesExactly 33 | 34 | it "only knows the funky chicken by default" do 35 | assert_that My::Funky::Class.new, has_attribute(:dances, includes_exactly(:funky_chicken)) 36 | end 37 | end 38 | ```` 39 | 40 | This matches an object that has an attribute (actually just a no arg method) 41 | called `dances` where the result of calling that method is an `Enumerable` that 42 | includes only the symbol `:funky_chicken`. 43 | 44 | What matchers are included? 45 | --------------------------- 46 | 47 | This is the list of current matchers. This list will grow over time and some 48 | names might change in order to allow for integration into various testing 49 | frameworks. 50 | 51 | * `Ramcrest::Aint` - Logical negation. 52 | 53 | ````ruby 54 | assert_that 1, aint(2) 55 | assert_that [2], aint(includes_exactly(1)) 56 | ```` 57 | 58 | * `Ramcrest::Anything` - Everything's OK! 59 | 60 | ````ruby 61 | assert_that "a value", anything 62 | ```` 63 | 64 | * `Ramcrest::Comparable` - Matchers for ordering comparisons. 65 | 66 | ````ruby 67 | assert_that 1, greater_than(0) 68 | assert_that 2, less_than(3) 69 | assert_that 3, greater_or_equal_to(3) 70 | assert_that 4, less_or_equal_to(4) 71 | ```` 72 | 73 | * `Ramcrest::EqualTo` - Equality (via `==`). 74 | 75 | ````ruby 76 | assert_that "my value", equal_to("my value") 77 | ```` 78 | 79 | * `Ramcrest::HasAttribute` - Object attribute matching. 80 | 81 | ````ruby 82 | dance = Struct.new(:twist).new(2) 83 | assert_that dance, has_attribute(:funk) # the attribute exits 84 | assert_that dance, has_attribute(:funk, equal_to(2)) # the attribute exists with a value 85 | ```` 86 | 87 | * `Ramcrest::HasSize` - Collection size. 88 | 89 | ````ruby 90 | assert_that [1, 2], has_size(2) 91 | assert_that { :a => 1 }, has_size(less_or_equal_to(3)) 92 | ```` 93 | 94 | * `Ramcrest::Includes` - Enumerable inclusion. 95 | 96 | ````ruby 97 | assert_that [1, 2, 3], includes(2, 3) 98 | assert_that [6, 7, 2], includes(6, greater_than(6)) 99 | ```` 100 | 101 | * `Ramcrest::IncludesExactly` - Enumerable equality. 102 | 103 | ````ruby 104 | assert_that [1, 2, 3], includes_exactly(1, 2, 3) 105 | assert_that [3, 2, 1], aint(includes_exactly(1, 2, 3)) 106 | assert_that [1], aint(includes_exactly(1, 2)) 107 | ```` 108 | 109 | * `Ramcrest::IncludesInAnyOrderExactly` - Enumerable set equality. 110 | 111 | ````ruby 112 | assert_that [2, 1, 3], includes_in_any_order_exactly(1, 2, 3) 113 | assert_that [1, 3, 4], aint(includes_in_any_order_exactly(1, 3)) 114 | ```` 115 | 116 | * `Ramcrest::Is` - Syntactic sugar for equality. 117 | 118 | ````ruby 119 | assert_that 1, is(1) 120 | assert_that 2, is(greater_than(1)) 121 | ```` 122 | 123 | * `Ramcrest::SuchThat` - Ad hoc matchers. 124 | 125 | ````ruby 126 | assert_that "my string", such_that do |value| 127 | value =~ /string/ ? success : mismatch("didn't contain 'string'") 128 | end 129 | ```` 130 | 131 | Writing your own matchers 132 | ------------------------- 133 | 134 | The simplest way to get started writing your own own matchers is to just make 135 | ad-hoc matchers using `such_that` and chained match results. For example to put 136 | together a matcher for a certain type that has two properties you can simply 137 | do: 138 | 139 | ````ruby 140 | def a_token(with_attributes) 141 | name = has_attribute(:name, equal_to(with_attributes[:named])) 142 | string = has_attribute(:string, equal_to(with_attributes[:string])) 143 | description = "a token named <#{with_attributes[:named]}> with string <#{with_attributes[:string]}>" 144 | 145 | such_that(description) do |actual| 146 | name.matches?(actual).and_also { string.matches?(actual) } 147 | end 148 | end 149 | ```` 150 | 151 | Using `such_that` should be able to allow you to write any matcher that you 152 | want in a simple and straight-forward way. If you outgrow these kinds of 153 | matchers and want to move onto much larger possiblities, then you just need to 154 | implement a class with two methods: `matches?(actual)` and `descriptionf`. The 155 | `matches?` method needs to return either `success` or `mismatch(description)`. 156 | 157 | ````ruby 158 | class AToken 159 | include Ramcrest::Matcher 160 | 161 | def initialize(name) 162 | @matchers = [Ramcrest::HasAttribute.has_attribute(:name, equal_to(name))] 163 | end 164 | 165 | def with_string(string) 166 | @matchers << Ramcrest::HasAttribute.has_attribute(:string, equal_to(string)) 167 | self 168 | end 169 | 170 | def matches?(actual) 171 | @matchers. 172 | collect { |matcher| matcher.matches?(actual) }. 173 | find(method(:success)) { |result| !result.matched? } 174 | end 175 | 176 | def description 177 | "a token that #{@matchers.collect(&:description).join(' and ')}" 178 | end 179 | end 180 | 181 | def a_token_named(name) 182 | AToken.new(name) 183 | end 184 | 185 | assert_that something, is(a_token_named("foo").with_string("bar")) 186 | assert_that something_else, is(a_token_named("baz")) 187 | ```` 188 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | require 'rake/clean' 3 | 4 | Rake::TestTask.new do |t| 5 | t.pattern = 'test/**/*.rb' 6 | end 7 | 8 | Rake::TestTask.new(:spec) do |t| 9 | t.pattern = 'spec/**/*.rb' 10 | end 11 | 12 | task :default => [:test, :spec] 13 | 14 | CLOBBER.include('ramcrest*.gem') 15 | task :package do 16 | `gem build ramcrest.gemspec` 17 | end 18 | -------------------------------------------------------------------------------- /lib/ramcrest.rb: -------------------------------------------------------------------------------- 1 | module Ramcrest 2 | require 'ramcrest/minitest_assert' 3 | require 'ramcrest/rspec' 4 | require 'ramcrest/equal_to' 5 | require 'ramcrest/comparable' 6 | require 'ramcrest/is' 7 | require 'ramcrest/aint' 8 | require 'ramcrest/anything' 9 | require 'ramcrest/has_attribute' 10 | require 'ramcrest/has_size' 11 | require 'ramcrest/includes' 12 | require 'ramcrest/includes_exactly' 13 | require 'ramcrest/includes_in_any_order_exactly' 14 | require 'ramcrest/such_that' 15 | require 'ramcrest/matcher_matcher' 16 | 17 | def self.is_matcher?(possible_matcher) 18 | possible_matcher.respond_to?(:matches?) && possible_matcher.respond_to?(:description) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/ramcrest/aint.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/matcher' 2 | 3 | module Ramcrest 4 | module Aint 5 | module_function 6 | def aint(expected) 7 | Matcher.new(Ramcrest::EqualTo.to_matcher(expected)) 8 | end 9 | 10 | class Matcher 11 | include Ramcrest::Matcher 12 | 13 | def initialize(expected) 14 | @expected = expected 15 | end 16 | 17 | def do_match(expected, actual) 18 | super.negate 19 | end 20 | 21 | def mismatch_message(actual, match) 22 | "<#{actual}>" 23 | end 24 | 25 | def description 26 | "not #{@expected.description}" 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/ramcrest/anything.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/matcher' 2 | 3 | module Ramcrest 4 | module Anything 5 | module_function 6 | 7 | def anything 8 | Matcher.new 9 | end 10 | 11 | class Matcher 12 | include Ramcrest::Matcher 13 | 14 | def matches?(actual) 15 | success 16 | end 17 | 18 | def description 19 | "anything" 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/ramcrest/assertion.rb: -------------------------------------------------------------------------------- 1 | module Ramcrest 2 | module Assertion 3 | def self.assert_match(actual, matcher, &failure_handler) 4 | match = matcher.matches?(actual) 5 | if not match.matched? 6 | failure_handler.call("expected: #{matcher.description}\nbut: #{match.description}") 7 | end 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/ramcrest/base_enumerable_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/matcher' 2 | 3 | module Ramcrest 4 | module Enumerable 5 | class BaseEnumerableMatcher 6 | include Ramcrest::Matcher 7 | 8 | def initialize(match_type_description, expected, size_matcher) 9 | @match_type_description = match_type_description 10 | @expected = expected 11 | @size_matcher = Ramcrest::HasSize.has_size(size_matcher) 12 | end 13 | 14 | def matches?(actual) 15 | size_match = @size_matcher.matches?(actual) 16 | if !size_match.matched? 17 | return mismatch("an enumerable of #{size_match.description}. Enumerable was #{show(actual)}.") 18 | end 19 | 20 | unmatched = unmatched_expectations_from(actual) 21 | if unmatched.empty? 22 | success 23 | else 24 | mismatch("an enumerable that does not include #{describe(unmatched)}. Enumerable was #{show(actual)}.") 25 | end 26 | end 27 | 28 | def description 29 | "an enumerable #{@match_type_description} #{description_of_expecteds}" 30 | end 31 | 32 | private 33 | 34 | def show(array) 35 | "[#{array.join(', ')}]" 36 | end 37 | 38 | def description_of_expecteds 39 | describe(@expected) 40 | end 41 | 42 | def describe(matchers) 43 | show(matchers.collect(&:description)) 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/ramcrest/comparable.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/matcher' 2 | 3 | module Ramcrest 4 | module Comparable 5 | module_function 6 | 7 | def greater_than(expected) 8 | Matcher.new("greater than", :>, expected) 9 | end 10 | 11 | def greater_or_equal_to(expected) 12 | Matcher.new("greater than or equal to", :>=, expected) 13 | end 14 | 15 | def less_than(expected) 16 | Matcher.new("less than", :<, expected) 17 | end 18 | 19 | def less_or_equal_to(expected) 20 | Matcher.new("less than or equal to", :<=, expected) 21 | end 22 | 23 | class Matcher 24 | include Ramcrest::Matcher 25 | 26 | def initialize(name, operator, expected) 27 | @name = name 28 | @operator = operator 29 | @expected = expected 30 | end 31 | 32 | def matches?(actual) 33 | if actual.send(@operator, @expected) 34 | success 35 | else 36 | mismatch("was <#{actual}>") 37 | end 38 | end 39 | 40 | def description 41 | "#{@name} <#{@expected}>" 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/ramcrest/equal_to.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/matcher' 2 | 3 | module Ramcrest 4 | module EqualTo 5 | def self.to_matcher(possible_matcher) 6 | return possible_matcher if Ramcrest.is_matcher?(possible_matcher) 7 | Ramcrest::EqualTo.equal_to(possible_matcher) 8 | end 9 | 10 | module_function 11 | 12 | def equal_to(expected) 13 | Matcher.new(expected) 14 | end 15 | 16 | class Matcher 17 | include Ramcrest::Matcher 18 | 19 | def initialize(expected) 20 | @expected = expected 21 | end 22 | 23 | def do_match(expected, actual) 24 | MatchResult.new(expected == actual) 25 | end 26 | 27 | def mismatch_message(actual, match) 28 | "#{actual}" 29 | end 30 | 31 | def description 32 | "#{@expected}" 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/ramcrest/has_attribute.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/matcher' 2 | require 'ramcrest/anything' 3 | 4 | module Ramcrest 5 | module HasAttribute 6 | module_function 7 | 8 | def has_attribute(attribute_name, value_matcher = Ramcrest::Anything.anything()) 9 | Matcher.new(attribute_name, value_matcher) 10 | end 11 | 12 | class Matcher 13 | include Ramcrest::Matcher 14 | 15 | def initialize(attribute_name, value_matcher) 16 | @attribute_name = attribute_name 17 | @value_matcher = value_matcher 18 | end 19 | 20 | def matches?(actual) 21 | if actual.respond_to?(@attribute_name) 22 | match = @value_matcher.matches?(actual.send(@attribute_name)) 23 | if match.matched? 24 | return success 25 | else 26 | return mismatch("object <#{actual}> attribute '#{@attribute_name}' #{match.description}") 27 | end 28 | else 29 | return mismatch("object <#{actual}> was missing attribute '#{@attribute_name}'") 30 | end 31 | end 32 | 33 | def description 34 | "an object with an attribute named '#{@attribute_name}' and value #{@value_matcher.description}" 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/ramcrest/has_size.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/matcher' 2 | require 'ramcrest/is' 3 | 4 | module Ramcrest 5 | module HasSize 6 | module_function 7 | def has_size(expected) 8 | Matcher.new(Ramcrest::Is.to_matcher(expected)) 9 | end 10 | 11 | class Matcher 12 | include Ramcrest::Matcher 13 | 14 | def initialize(expected) 15 | @expected = expected 16 | end 17 | 18 | def do_match(expected, actual) 19 | super expected, actual.size 20 | end 21 | 22 | def mismatch_message(actual, match) 23 | "size #{match.description}" 24 | end 25 | 26 | def description 27 | "an enumerable of size #{@expected.description}" 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/ramcrest/includes.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/base_enumerable_matcher' 2 | 3 | module Ramcrest 4 | module Includes 5 | module_function 6 | 7 | def includes(*expected) 8 | expected_matchers = expected.collect(&Ramcrest::EqualTo.method(:to_matcher)) 9 | Matcher.new(expected_matchers) 10 | end 11 | 12 | class Matcher < Ramcrest::Enumerable::BaseEnumerableMatcher 13 | def initialize(expected) 14 | super("including", 15 | expected, 16 | Ramcrest::Comparable.greater_or_equal_to(expected.size)) 17 | end 18 | 19 | protected 20 | 21 | def unmatched_expectations_from(actual) 22 | actual.inject(@expected) do |left_to_expect, value| 23 | matched = matcher_that_matches(value, left_to_expect) 24 | no_longer_expect(matched, left_to_expect) 25 | end 26 | end 27 | 28 | def matcher_that_matches(value, expected) 29 | expected.find { |expect| expect.matches?(value).matched? } 30 | end 31 | 32 | def no_longer_expect(matched, expected) 33 | expected.reject { |expect| expect == matched } 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/ramcrest/includes_exactly.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/base_enumerable_matcher' 2 | 3 | module Ramcrest 4 | module IncludesExactly 5 | module_function 6 | 7 | def includes_exactly(*expected) 8 | expected_matchers = expected.collect(&Ramcrest::EqualTo.method(:to_matcher)) 9 | Matcher.new(expected_matchers) 10 | end 11 | 12 | class Matcher < Ramcrest::Enumerable::BaseEnumerableMatcher 13 | def initialize(expected) 14 | super("including exactly", 15 | expected, 16 | Ramcrest::EqualTo.equal_to(expected.size)) 17 | end 18 | 19 | protected 20 | 21 | def unmatched_expectations_from(actual) 22 | unmatched = actual.zip(@expected).drop_while do |pair| 23 | pair[1].matches?(pair[0]).matched? 24 | end 25 | unmatched.collect { |pair| pair[1] } 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/ramcrest/includes_in_any_order_exactly.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/base_enumerable_matcher' 2 | 3 | module Ramcrest 4 | module IncludesInAnyOrderExactly 5 | module_function 6 | 7 | def includes_in_any_order_exactly(*expected) 8 | expected_matchers = expected.collect(&Ramcrest::EqualTo.method(:to_matcher)) 9 | Matcher.new(expected_matchers) 10 | end 11 | 12 | class Matcher < Ramcrest::Enumerable::BaseEnumerableMatcher 13 | def initialize(expected) 14 | super("including in any order exactly", 15 | expected, 16 | Ramcrest::EqualTo.equal_to(expected.size)) 17 | end 18 | 19 | protected 20 | 21 | def unmatched_expectations_from(actual) 22 | actual.inject(@expected) do |left_to_expect, value| 23 | matched = matcher_that_matches(value, left_to_expect) 24 | no_longer_expect(matched, left_to_expect) 25 | end 26 | end 27 | 28 | def matcher_that_matches(value, expected) 29 | expected.find { |expect| expect.matches?(value).matched? } 30 | end 31 | 32 | def no_longer_expect(matched, expected) 33 | expected.reject { |expect| expect == matched } 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/ramcrest/is.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/matcher' 2 | 3 | module Ramcrest 4 | module Is 5 | def self.to_matcher(possible_matcher) 6 | return possible_matcher if Ramcrest.is_matcher?(possible_matcher) 7 | Ramcrest::Is.is(possible_matcher) 8 | end 9 | 10 | module_function 11 | 12 | def is(expected) 13 | Matcher.new(Ramcrest::EqualTo.to_matcher(expected), Ramcrest.is_matcher?(expected)) 14 | end 15 | 16 | class Matcher 17 | include Ramcrest::Matcher 18 | 19 | def initialize(expected, was_originally_matcher) 20 | @expected = expected 21 | @was_originally_matcher = was_originally_matcher 22 | end 23 | 24 | def mismatch_message(actual, match) 25 | "was #{show match.description}" 26 | end 27 | 28 | def description 29 | "is #{show @expected.description}" 30 | end 31 | 32 | private 33 | 34 | def show(text) 35 | if @was_originally_matcher 36 | text 37 | else 38 | "<#{text}>" 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/ramcrest/matcher.rb: -------------------------------------------------------------------------------- 1 | module Ramcrest 2 | module Matcher 3 | 4 | def do_match(expected, actual) 5 | expected.matches?(actual) 6 | end 7 | 8 | def matches?(actual) 9 | match = do_match(@expected, actual) 10 | if match.matched? 11 | success 12 | else 13 | mismatch(mismatch_message(actual, match)) 14 | end 15 | end 16 | 17 | def success 18 | MatchResult::SUCCESS 19 | end 20 | 21 | def mismatch(description) 22 | MatchResult.new(false, description) 23 | end 24 | 25 | class MatchResult 26 | attr_reader :description 27 | 28 | def initialize(matched, description = nil) 29 | @matched = matched 30 | @description = description 31 | end 32 | 33 | def negate 34 | MatchResult.new(!@matched, @description) 35 | end 36 | 37 | def or_else(&mismatch_block) 38 | if matched? 39 | self 40 | else 41 | yield self 42 | end 43 | end 44 | 45 | def and_also(&continue_block) 46 | if matched? 47 | yield 48 | else 49 | self 50 | end 51 | end 52 | 53 | def matched? 54 | @matched 55 | end 56 | 57 | SUCCESS = MatchResult.new(true) 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/ramcrest/matcher_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/matcher' 2 | 3 | module Ramcrest 4 | module MatcherMatcher 5 | module_function 6 | def a_matcher_that_matches(value) 7 | MatcherThatMatchesMatcher.new(value) 8 | end 9 | 10 | def a_matcher_described_as(description) 11 | MatcherDescribedAsMatcher.new(description) 12 | end 13 | 14 | def a_matcher_that_mismatches(value, description) 15 | MatcherThatMismatchesMatcher.new(value, description) 16 | end 17 | 18 | class MatcherThatMatchesMatcher 19 | include Ramcrest::Matcher 20 | 21 | def initialize(value) 22 | @value = value 23 | end 24 | 25 | def matches?(matcher) 26 | match = matcher.matches?(@value) 27 | if match.matched? 28 | success 29 | else 30 | mismatch("a matcher describing itself as <#{matcher.description}> that mismatched with <#{match.description}>") 31 | end 32 | end 33 | 34 | def description 35 | "a matcher that matches <#{@value}>" 36 | end 37 | end 38 | 39 | class MatcherDescribedAsMatcher 40 | include Ramcrest::Matcher 41 | 42 | def initialize(description) 43 | @description = description 44 | end 45 | 46 | def matches?(matcher) 47 | if matcher.description == @description 48 | success 49 | else 50 | mismatch("a matcher that described itself as \"#{matcher.description}\"") 51 | end 52 | end 53 | 54 | def description 55 | "a matcher described as \"#{@description}\"" 56 | end 57 | end 58 | 59 | class MatcherThatMismatchesMatcher 60 | include Ramcrest::Matcher 61 | 62 | def initialize(value, description) 63 | @value = value 64 | @description = description 65 | end 66 | 67 | def matches?(matcher) 68 | match = matcher.matches?(@value) 69 | if match.matched? 70 | mismatch("the matcher matched") 71 | else 72 | if match.description == @description 73 | success 74 | else 75 | mismatch("the mismatch description was \"#{match.description}\"") 76 | end 77 | end 78 | end 79 | 80 | def description 81 | "a matcher that mismatches <#{@value}> explained by \"#{@description}\"" 82 | end 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/ramcrest/minitest_assert.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/assertion' 2 | 3 | module MiniTest 4 | module Assertions 5 | def assert_that(object, matcher) 6 | Ramcrest::Assertion.assert_match(object, matcher) do |description| 7 | assert false, description 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/ramcrest/rspec.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/assertion' 2 | 3 | module RSpec 4 | module Expectations 5 | class ExpectationTarget 6 | def that(matcher) 7 | Ramcrest::Assertion.assert_match(@target, matcher) do |description| 8 | ::RSpec::Expectations.fail_with(description) 9 | end 10 | end 11 | end 12 | end 13 | end 14 | 15 | -------------------------------------------------------------------------------- /lib/ramcrest/such_that.rb: -------------------------------------------------------------------------------- 1 | require 'ramcrest/matcher' 2 | 3 | module Ramcrest 4 | module SuchThat 5 | module_function 6 | 7 | def such_that(description = "", &matcher_block) 8 | Matcher.new(description, matcher_block) 9 | end 10 | 11 | class Matcher 12 | include Ramcrest::Matcher 13 | 14 | attr_reader :description 15 | 16 | def initialize(description, matcher_block) 17 | singleton = class << self; self; end 18 | singleton.send(:define_method, :matches?, matcher_block) 19 | @description = description 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /ramcrest.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = 'ramcrest' 3 | s.version = '0.1.0' 4 | s.summary = 'Powerful, composable matchers for Ruby' 5 | s.authors = ['Andrew Parker'] 6 | s.email = ['aparker42@gmail.com'] 7 | s.homepage = 'https://github.com/hamcrest/ramcrest' 8 | s.files = Dir.glob('lib/**/*.rb') 9 | s.test_files = Dir.glob('test/**/*') 10 | s.description = <<-EOD 11 | Ramcrest is an implementation of Hamcrest for Ruby. It integrates with 12 | MinitTest and RSpec. Writing new matchers is easy! Why not give it a try? 13 | EOD 14 | end 15 | 16 | -------------------------------------------------------------------------------- /spec/rspec_integration_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest do 5 | include Ramcrest::HasAttribute 6 | include Ramcrest::Aint 7 | include Ramcrest::EqualTo 8 | include Ramcrest::Is 9 | 10 | it "uses a #that method on #expect for integrating with RSpec" do 11 | expect(1).that aint(nil) 12 | expect(Struct.new(:dance).new(2)).that has_attribute(:dance, equal_to(2)) 13 | end 14 | 15 | it "fails the test when the matcher does not match" do 16 | expect { expect(1).that is(nil) }.to raise_error(RSpec::Expectations::ExpectationNotMetError, /was <1>/) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/aint_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::Aint do 5 | include Ramcrest::Aint 6 | include Ramcrest::Is 7 | include Ramcrest::MatcherMatcher 8 | 9 | it "matches objects that are the different" do 10 | assert_that aint(is(1)), a_matcher_that_matches(2) 11 | end 12 | 13 | it "coerces values into an is() matcher" do 14 | assert_that aint(1), a_matcher_that_matches(2) 15 | end 16 | 17 | it "matches objects that are the different" do 18 | assert_that aint(is(1)), a_matcher_described_as("not is <1>") 19 | end 20 | 21 | it "does not match objects that are the same" do 22 | assert_that aint(is(1)), a_matcher_that_mismatches(1, "<1>") 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/anything_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::Anything do 5 | include Ramcrest::MatcherMatcher 6 | include Ramcrest::Anything 7 | 8 | it "always matches" do 9 | assert_that anything(), a_matcher_that_matches(nil) 10 | assert_that anything(), a_matcher_that_matches({}) 11 | end 12 | 13 | it "describes itself" do 14 | assert_that anything(), a_matcher_described_as("anything") 15 | end 16 | end 17 | 18 | -------------------------------------------------------------------------------- /test/assert_that_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe "assert_that" do 5 | include Ramcrest::Is 6 | 7 | it "passes when the match succeeds" do 8 | assert_that nil, is(nil) 9 | end 10 | 11 | it "raises when the match fails" do 12 | assert_raises(MiniTest::Assertion) { assert_that 1, is(2) } 13 | end 14 | 15 | it "combines the mismatch description with the matchers description for the message" do 16 | exception = assert_raises(MiniTest::Assertion) { assert_that 1, is(2) } 17 | assert_equal "expected: is <2>\nbut: was <1>", exception.message 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/comparable_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::Comparable do 5 | include Ramcrest::MatcherMatcher 6 | include Ramcrest::Comparable 7 | 8 | it "matches greater than" do 9 | assert_that greater_than(1), a_matcher_that_matches(2) 10 | assert_that greater_than(1), a_matcher_that_mismatches(1, "was <1>") 11 | assert_that greater_than(1), a_matcher_described_as("greater than <1>") 12 | end 13 | 14 | it "matches less than" do 15 | assert_that less_than(1), a_matcher_that_matches(0) 16 | assert_that less_than(1), a_matcher_that_mismatches(1, "was <1>") 17 | assert_that less_than(1), a_matcher_described_as("less than <1>") 18 | end 19 | 20 | it "matches greater than or equal" do 21 | assert_that greater_or_equal_to(1), a_matcher_that_matches(2) 22 | assert_that greater_or_equal_to(1), a_matcher_that_matches(1) 23 | assert_that greater_or_equal_to(1), a_matcher_that_mismatches(0, "was <0>") 24 | assert_that greater_or_equal_to(1), a_matcher_described_as("greater than or equal to <1>") 25 | end 26 | 27 | it "matches less than or equal" do 28 | assert_that less_or_equal_to(1), a_matcher_that_matches(0) 29 | assert_that less_or_equal_to(1), a_matcher_that_matches(1) 30 | assert_that less_or_equal_to(1), a_matcher_that_mismatches(2, "was <2>") 31 | assert_that less_or_equal_to(1), a_matcher_described_as("less than or equal to <1>") 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/has_attribute_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::HasAttribute do 5 | include Ramcrest::MatcherMatcher 6 | include Ramcrest::HasAttribute 7 | include Ramcrest::Is 8 | 9 | describe "when not matching the value" do 10 | it "matches an object with the attribute" do 11 | obj = an_object_with_attribute(:foo) 12 | 13 | assert_that has_attribute(:foo), a_matcher_that_matches(obj) 14 | end 15 | 16 | it "does not match an object that does not have the attribute" do 17 | obj = an_object_with_attribute(:different) 18 | 19 | assert_that has_attribute(:foo), 20 | a_matcher_that_mismatches(obj, "object <#{obj}> was missing attribute 'foo'") 21 | end 22 | 23 | it "describes itself" do 24 | assert_that has_attribute(:foo), 25 | a_matcher_described_as("an object with an attribute named 'foo' and value anything") 26 | end 27 | end 28 | 29 | describe "when matching the value" do 30 | it "matches an object with the attribute and the value" do 31 | obj = an_object_with_attribute(:foo, 1) 32 | 33 | assert_that has_attribute(:foo, is(1)), a_matcher_that_matches(obj) 34 | end 35 | 36 | it "does not match an object that does not have the attribute" do 37 | obj = an_object_with_attribute(:different) 38 | 39 | assert_that has_attribute(:foo, is(1)), 40 | a_matcher_that_mismatches(obj, "object <#{obj}> was missing attribute 'foo'") 41 | end 42 | 43 | it "does not match an object that has a different value" do 44 | obj = an_object_with_attribute(:foo, 2) 45 | 46 | assert_that has_attribute(:foo, is(1)), 47 | a_matcher_that_mismatches(obj, "object <#{obj}> attribute 'foo' was <2>") 48 | end 49 | 50 | it "describes itself" do 51 | assert_that has_attribute(:foo, is(1)), 52 | a_matcher_described_as("an object with an attribute named 'foo' and value is <1>") 53 | end 54 | end 55 | 56 | def an_object_with_attribute(name, value = nil) 57 | Struct.new(name).new(value) 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /test/has_size_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::HasSize do 5 | include Ramcrest::MatcherMatcher 6 | include Ramcrest::Is 7 | include Ramcrest::HasSize 8 | 9 | it "matches objects with a matching size" do 10 | assert_that has_size(1), a_matcher_that_matches([1]) 11 | assert_that has_size(1), a_matcher_that_matches({ :a => 1 }) 12 | end 13 | 14 | it "matches using other matchers" do 15 | assert_that has_size(Ramcrest::SuchThat.such_that { |size| success }), 16 | a_matcher_that_matches([2, 3]) 17 | end 18 | 19 | it "does not match objects that are different size" do 20 | assert_that has_size(1), a_matcher_that_mismatches([1, 2], "size was <2>") 21 | end 22 | 23 | it "describes itself" do 24 | assert_that has_size(1), a_matcher_described_as("an enumerable of size is <1>") 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/includes_exactly_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::IncludesExactly do 5 | include Ramcrest::MatcherMatcher 6 | include Ramcrest::Is 7 | include Ramcrest::IncludesExactly 8 | 9 | it "matches enumerables that have all of the elements in order" do 10 | assert_that includes_exactly(is(1), is(2)), a_matcher_that_matches([1, 2]) 11 | end 12 | 13 | it "coerces values into equality matches" do 14 | assert_that includes_exactly(1, 2), a_matcher_that_matches([1, 2]) 15 | end 16 | 17 | it "does not match when the elements are out of order" do 18 | assert_that includes_exactly(is(1), is(2)), 19 | a_matcher_that_mismatches([2, 1], "an enumerable that does not include [is <1>, is <2>]. Enumerable was [2, 1].") 20 | end 21 | 22 | it "does not match when there is an unexpected element" do 23 | assert_that includes_exactly(is(1), is(2)), 24 | a_matcher_that_mismatches([1, 3], "an enumerable that does not include [is <2>]. Enumerable was [1, 3].") 25 | end 26 | 27 | it "does not match when there are fewer elements than expected" do 28 | assert_that includes_exactly(is(1), is(2)), 29 | a_matcher_that_mismatches([1], "an enumerable of size 1. Enumerable was [1].") 30 | end 31 | 32 | it "does not match when there are more elements than expected" do 33 | assert_that includes_exactly(is(1), is(2)), 34 | a_matcher_that_mismatches([1, 2, 3], "an enumerable of size 3. Enumerable was [1, 2, 3].") 35 | end 36 | 37 | it "describes itself" do 38 | assert_that includes_exactly(is(1), is(2)), 39 | a_matcher_described_as("an enumerable including exactly [is <1>, is <2>]") 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/includes_in_any_order_exactly_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::IncludesInAnyOrderExactly do 5 | include Ramcrest::MatcherMatcher 6 | include Ramcrest::Is 7 | include Ramcrest::IncludesInAnyOrderExactly 8 | 9 | it "matches enumerables that have all of the elements" do 10 | assert_that includes_in_any_order_exactly(is(1), is(2)), a_matcher_that_matches([2, 1]) 11 | end 12 | 13 | it "coerces values into equality matches" do 14 | assert_that includes_in_any_order_exactly(1, 2), a_matcher_that_matches([2, 1]) 15 | end 16 | 17 | it "does not match when there is an unexpected element" do 18 | assert_that includes_in_any_order_exactly(is(1), is(2)), 19 | a_matcher_that_mismatches([1, 3], "an enumerable that does not include [is <2>]. Enumerable was [1, 3].") 20 | end 21 | 22 | it "does not match when there are fewer elements than expected" do 23 | assert_that includes_in_any_order_exactly(is(1), is(2)), 24 | a_matcher_that_mismatches([1], "an enumerable of size 1. Enumerable was [1].") 25 | end 26 | 27 | it "does not match when there are more elements than expected" do 28 | assert_that includes_in_any_order_exactly(is(1), is(2)), 29 | a_matcher_that_mismatches([1, 2, 3], "an enumerable of size 3. Enumerable was [1, 2, 3].") 30 | end 31 | 32 | it "describes itself" do 33 | assert_that includes_in_any_order_exactly(is(1), is(2)), 34 | a_matcher_described_as("an enumerable including in any order exactly [is <1>, is <2>]") 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/includes_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::Includes do 5 | include Ramcrest::MatcherMatcher 6 | include Ramcrest::Is 7 | include Ramcrest::Includes 8 | 9 | it "matches enumerables that have all of the elements" do 10 | assert_that includes(is(1), is(2)), a_matcher_that_matches([1, 3, 2]) 11 | end 12 | 13 | it "coerces values into equality matches" do 14 | assert_that includes(1, 2), a_matcher_that_matches([1, 3, 2]) 15 | end 16 | 17 | it "does not match when there is an unexpected element" do 18 | assert_that includes(is(1), is(2)), 19 | a_matcher_that_mismatches([1, 3], "an enumerable that does not include [is <2>]. Enumerable was [1, 3].") 20 | end 21 | 22 | it "does not match when there are fewer elements than expected" do 23 | assert_that includes(is(1), is(2)), 24 | a_matcher_that_mismatches([1], "an enumerable of size was <1>. Enumerable was [1].") 25 | end 26 | 27 | it "describes itself" do 28 | assert_that includes(is(1), is(2)), 29 | a_matcher_described_as("an enumerable including [is <1>, is <2>]") 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/is_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::Is do 5 | include Ramcrest::MatcherMatcher 6 | include Ramcrest::Is 7 | 8 | it "matches objects that are the same" do 9 | assert_that is(1), a_matcher_that_matches(1) 10 | end 11 | 12 | it "matches using other matchers" do 13 | assert_that is(is(1)), a_matcher_that_matches(1) 14 | end 15 | 16 | it "does not match objects that are different" do 17 | assert_that is(1), a_matcher_that_mismatches(2, "was <2>") 18 | end 19 | 20 | it "describes itself" do 21 | assert_that is(1), a_matcher_described_as("is <1>") 22 | end 23 | 24 | describe "when composed with another matcher" do 25 | it "describes itself without extra marks" do 26 | assert_that is(is(1)), a_matcher_described_as("is is <1>") 27 | end 28 | 29 | it "does not describe a mismatch with extra marks" do 30 | assert_that is(is(1)), a_matcher_that_mismatches(2, "was was <2>") 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/matcher_matcher_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::MatcherMatcher do 5 | include Ramcrest::MatcherMatcher 6 | 7 | INCORRECT_VALUE = 2 8 | INCORRECT_DESCRIPTION = "wrong description" 9 | CORRECT_VALUE = 1 10 | 11 | MATCHER = Ramcrest::Is.is(CORRECT_VALUE) 12 | MATCHER_DESCRIPTION = MATCHER.description 13 | MISMATCH_DESCRIPTION = MATCHER.matches?(INCORRECT_VALUE).description 14 | 15 | describe "#a_matcher_that_matches" do 16 | it "matches when the tested matcher matches" do 17 | assert_that MATCHER, a_matcher_that_matches(CORRECT_VALUE) 18 | end 19 | 20 | it "mismatches when the tested matcher does not match" do 21 | match = a_matcher_that_matches(INCORRECT_VALUE).matches?(MATCHER) 22 | 23 | assert !match.matched? 24 | end 25 | 26 | it "describes itself" do 27 | assert_equal "a matcher that matches <#{CORRECT_VALUE}>", a_matcher_that_matches(CORRECT_VALUE).description 28 | end 29 | 30 | it "describes the mismatch when the matcher does not match" do 31 | assert_equal "a matcher describing itself as <#{MATCHER_DESCRIPTION}> that mismatched with <#{MISMATCH_DESCRIPTION}>", a_matcher_that_matches(INCORRECT_VALUE).matches?(MATCHER).description 32 | end 33 | end 34 | 35 | describe "#a_matcher_described_as" do 36 | it "matches when the matcher's description matches" do 37 | assert_that MATCHER, a_matcher_described_as(MATCHER_DESCRIPTION) 38 | end 39 | 40 | it "mismatches when the matcher's description does not match" do 41 | match = a_matcher_described_as(INCORRECT_DESCRIPTION).matches?(MATCHER) 42 | 43 | assert !match.matched? 44 | end 45 | 46 | it "describes itself" do 47 | assert_equal "a matcher described as \"#{MATCHER_DESCRIPTION}\"", a_matcher_described_as(MATCHER_DESCRIPTION).description 48 | end 49 | 50 | it "describes the mismatch when the matcher does not match" do 51 | assert_equal "a matcher that described itself as \"#{MATCHER_DESCRIPTION}\"", a_matcher_described_as(INCORRECT_DESCRIPTION).matches?(MATCHER).description 52 | end 53 | end 54 | 55 | describe "#a_matcher_that_mismatches" do 56 | it "matches when the matcher mismatches with the wanted description" do 57 | assert_that MATCHER, a_matcher_that_mismatches(INCORRECT_VALUE, MISMATCH_DESCRIPTION) 58 | end 59 | 60 | it "mismatches when the matcher matches" do 61 | match = a_matcher_that_mismatches(CORRECT_VALUE, MISMATCH_DESCRIPTION).matches?(MATCHER) 62 | 63 | assert !match.matched? 64 | assert_equal "the matcher matched", match.description 65 | end 66 | 67 | it "mismatches when the matcher mismatched but provided the wrong mismatch description" do 68 | match = a_matcher_that_mismatches(INCORRECT_VALUE, "wrong mismatch desc").matches?(MATCHER) 69 | 70 | assert !match.matched? 71 | assert_equal "the mismatch description was \"#{MISMATCH_DESCRIPTION}\"", match.description 72 | end 73 | 74 | it "describes itself" do 75 | assert_equal "a matcher that mismatches <#{INCORRECT_VALUE}> explained by \"#{MISMATCH_DESCRIPTION}\"", a_matcher_that_mismatches(INCORRECT_VALUE, MISMATCH_DESCRIPTION).description 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /test/matcher_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest/matcher' 3 | 4 | describe Ramcrest::Matcher do 5 | include Ramcrest::Matcher 6 | 7 | it "a successful match is equivalent to true" do 8 | assert_equal true, success.matched? 9 | end 10 | 11 | it "an unsuccessful match is equivalent to false" do 12 | assert_equal false, mismatch("description").matched? 13 | end 14 | 15 | it "an unsuccessful match has a description" do 16 | assert_equal "description", mismatch("description").description 17 | end 18 | 19 | it "a successful match has no description" do 20 | assert_nil success.description 21 | end 22 | 23 | describe "#negate" do 24 | it "inverts the result" do 25 | assert_equal false, success.negate.matched? 26 | assert_equal true, mismatch("description").negate.matched? 27 | end 28 | 29 | it "does not change the original match" do 30 | original = mismatch("description") 31 | 32 | original.negate 33 | 34 | assert_equal false, original.matched? 35 | end 36 | end 37 | 38 | describe "when results are converted with #or_else" do 39 | it "returns success from #or_else when it matches" do 40 | assert_equal true, success.or_else { raise "not reached" }.matched? 41 | end 42 | 43 | it "calls the else block when it is a mismatch" do 44 | assert_raises(RuntimeError, "mismatch discription from block") do 45 | mismatch("mismatch description").or_else do |mismatch| 46 | raise RuntimeError, "#{mismatch.description} from block" 47 | end 48 | end 49 | end 50 | end 51 | 52 | describe "when chained with another match result via #and_also" do 53 | it "propogates successful matches" do 54 | assert_equal true, success.and_also { success }.matched? 55 | end 56 | 57 | it "short-circuits on a mismatch" do 58 | assert_equal false, mismatch("stop").and_also { raise "not reached" }.matched? 59 | end 60 | 61 | it "returns the result of the also" do 62 | assert_equal "the second step", success.and_also { mismatch("the second step") }.description 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /test/such_that_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ramcrest' 3 | 4 | describe Ramcrest::SuchThat do 5 | include Ramcrest::MatcherMatcher 6 | include Ramcrest::SuchThat 7 | 8 | it "matches when the such that block returns success" do 9 | assert_that such_that { |value| success }, a_matcher_that_matches(nil) 10 | end 11 | 12 | it "mismatches when the such that block returns mismatch" do 13 | assert_that such_that { |value| mismatch("wrong") }, a_matcher_that_mismatches(nil, "wrong") 14 | end 15 | 16 | it "describes itself by default as an anonymous matcher" do 17 | assert_that such_that { |value| }, a_matcher_described_as("") 18 | end 19 | 20 | it "allows for composing with other such_that matchers" do 21 | base = such_that { |value| if value > 2 then success else mismatch("no") end } 22 | delegating = such_that { |value| base.matches?(value) } 23 | 24 | assert_that delegating, a_matcher_that_matches(3) 25 | assert_that delegating, a_matcher_that_mismatches(2, "no") 26 | end 27 | 28 | it "can have a description provided" do 29 | assert_that such_that("something is true") { |value| }, 30 | a_matcher_described_as("something is true") 31 | end 32 | end 33 | --------------------------------------------------------------------------------