matchi 2.3.1 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +104 -77
- data/lib/matchi.rb +4 -2
- data/lib/matchi/be.rb +49 -0
- data/lib/matchi/be_an_instance_of.rb +49 -0
- data/lib/matchi/change.rb +109 -0
- data/lib/matchi/change/by.rb +63 -0
- data/lib/matchi/change/by_at_least.rb +63 -0
- data/lib/matchi/change/by_at_most.rb +63 -0
- data/lib/matchi/change/from.rb +44 -0
- data/lib/matchi/change/from/to.rb +69 -0
- data/lib/matchi/change/to.rb +62 -0
- data/lib/matchi/eq.rb +49 -0
- data/lib/matchi/match.rb +49 -0
- data/lib/matchi/raise_exception.rb +53 -0
- data/lib/matchi/satisfy.rb +49 -0
- metadata +17 -16
- data/lib/matchi/helper.rb +0 -40
- data/lib/matchi/matcher.rb +0 -11
- data/lib/matchi/matcher/base.rb +0 -63
- data/lib/matchi/matcher/be_an_instance_of.rb +0 -51
- data/lib/matchi/matcher/be_false.rb +0 -24
- data/lib/matchi/matcher/be_nil.rb +0 -24
- data/lib/matchi/matcher/be_true.rb +0 -24
- data/lib/matchi/matcher/eql.rb +0 -35
- data/lib/matchi/matcher/equal.rb +0 -35
- data/lib/matchi/matcher/match.rb +0 -35
- data/lib/matchi/matcher/raise_exception.rb +0 -39
- data/lib/matchi/matcher/satisfy.rb +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 294443e7f304a5c154aa48af9c20145bba9ff2caaf090bb7336c9bb7538be19d
|
4
|
+
data.tar.gz: c89f84d45eaf0923a7a023bd11b9cf54830054a178ce93e1074aea63cb7a9658
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9fe569142636d1acbba97f47dc7efbe38da3c02e9b19e77ab7ad723b6c03a52c03c3d8cd730a9948b4c45b21cdb6e807c29c181fd5d27f77aeee11c92c9738f4
|
7
|
+
data.tar.gz: 2bd24bb8f545496e7bbb115f9bd1577f60d03e67198a0452cd2393f977f063ac0e168f699930c6819a0c478a9c208627db75f7c49a63e5f5905256ba0ef3c8ca
|
data/README.md
CHANGED
@@ -6,9 +6,14 @@
|
|
6
6
|
[](https://github.com/fixrb/matchi/actions?query=workflow%3Arubocop+branch%3Amain)
|
7
7
|
[](https://github.com/fixrb/matchi/raw/main/LICENSE.md)
|
8
8
|
|
9
|
-
> Collection of expectation matchers for
|
9
|
+
> Collection of expectation matchers for Rubyists 🤹
|
10
10
|
|
11
|
-

|
12
|
+
|
13
|
+
## Project goals
|
14
|
+
|
15
|
+
* Adding matchers should be as simple as possible.
|
16
|
+
* Being framework agnostic and easy to integrate.
|
12
17
|
|
13
18
|
## Installation
|
14
19
|
|
@@ -44,91 +49,131 @@ require "matchi"
|
|
44
49
|
|
45
50
|
All examples here assume that this has been done.
|
46
51
|
|
52
|
+
### Anatomy of a matcher
|
53
|
+
|
54
|
+
A __Matchi__ matcher is an object that must respond to the `matches?` method with a block as argument, and return a boolean.
|
55
|
+
|
56
|
+
To facilitate the integration of the matchers in other tools, it is recommended to expose the expected value via the `expected` method.
|
57
|
+
|
58
|
+
That's all it is.
|
59
|
+
|
60
|
+
Let's see some examples.
|
61
|
+
|
47
62
|
### Built-in matchers
|
48
63
|
|
64
|
+
Here is the collection of useful generic matchers.
|
65
|
+
|
49
66
|
**Equivalence** matcher:
|
50
67
|
|
51
68
|
```ruby
|
52
|
-
|
53
|
-
|
69
|
+
matcher = Matchi::Eq.new("foo")
|
70
|
+
|
71
|
+
matcher.expected # => "foo"
|
72
|
+
matcher.matches? { "foo" } # => true
|
54
73
|
```
|
55
74
|
|
56
75
|
**Identity** matcher:
|
57
76
|
|
58
77
|
```ruby
|
59
|
-
|
60
|
-
|
78
|
+
matcher = Matchi::Be.new(:foo)
|
79
|
+
|
80
|
+
matcher.expected # => :foo
|
81
|
+
matcher.matches? { :foo } # => true
|
61
82
|
```
|
62
83
|
|
63
84
|
**Regular expressions** matcher:
|
64
85
|
|
65
86
|
```ruby
|
66
|
-
|
67
|
-
|
87
|
+
matcher = Matchi::Match.new(/^foo$/)
|
88
|
+
|
89
|
+
matcher.expected # => /^foo$/
|
90
|
+
matcher.matches? { "foo" } # => true
|
68
91
|
```
|
69
92
|
|
70
93
|
**Expecting errors** matcher:
|
71
94
|
|
72
95
|
```ruby
|
73
|
-
|
74
|
-
|
96
|
+
matcher = Matchi::RaiseException.new(:NameError)
|
97
|
+
|
98
|
+
matcher.expected # => "NameError"
|
99
|
+
matcher.matches? { Boom } # => true
|
75
100
|
```
|
76
101
|
|
77
|
-
**
|
102
|
+
**Type/class** matcher:
|
78
103
|
|
79
104
|
```ruby
|
80
|
-
|
81
|
-
|
105
|
+
matcher = Matchi::BeAnInstanceOf.new(:String)
|
106
|
+
|
107
|
+
matcher.expected # => "String"
|
108
|
+
matcher.matches? { "foo" } # => true
|
82
109
|
```
|
83
110
|
|
84
|
-
**
|
111
|
+
**Change** matcher:
|
85
112
|
|
86
113
|
```ruby
|
87
|
-
|
88
|
-
|
89
|
-
```
|
114
|
+
object = []
|
115
|
+
matcher = Matchi::Change.new(object, :length).by(1)
|
90
116
|
|
91
|
-
|
117
|
+
matcher.expected # => 1
|
118
|
+
matcher.matches? { object << 1 } # => true
|
92
119
|
|
93
|
-
|
94
|
-
|
95
|
-
be_nil.matches? { nil } # => true
|
96
|
-
```
|
120
|
+
object = []
|
121
|
+
matcher = Matchi::Change.new(object, :length).by_at_least(1)
|
97
122
|
|
98
|
-
|
123
|
+
matcher.expected # => 1
|
124
|
+
matcher.matches? { object << 1 } # => true
|
99
125
|
|
100
|
-
|
101
|
-
|
102
|
-
|
126
|
+
object = []
|
127
|
+
matcher = Matchi::Change.new(object, :length).by_at_most(1)
|
128
|
+
|
129
|
+
matcher.expected # => 1
|
130
|
+
matcher.matches? { object << 1 } # => true
|
131
|
+
|
132
|
+
object = "foo"
|
133
|
+
matcher = Matchi::Change.new(object, :to_s).from("foo").to("FOO")
|
134
|
+
|
135
|
+
matcher.expected # => "FOO"
|
136
|
+
matcher.matches? { object.upcase! } # => true
|
137
|
+
|
138
|
+
object = "foo"
|
139
|
+
matcher = Matchi::Change.new(object, :to_s).to("FOO")
|
140
|
+
|
141
|
+
matcher.expected # => "FOO"
|
142
|
+
matcher.matches? { object.upcase! } # => true
|
103
143
|
```
|
104
144
|
|
105
145
|
**Satisfy** matcher:
|
106
146
|
|
107
147
|
```ruby
|
108
|
-
|
109
|
-
|
148
|
+
matcher = Matchi::Satisfy.new { |value| value == 42 }
|
149
|
+
|
150
|
+
matcher.expected # => #<Proc:0x00007fbaafc65540>
|
151
|
+
matcher.matches? { 42 } # => true
|
110
152
|
```
|
111
153
|
|
112
154
|
### Custom matchers
|
113
155
|
|
114
|
-
Custom matchers can easily be
|
115
|
-
They can be any Ruby class that responds to `matches?` instance method with a block.
|
156
|
+
Custom matchers can easily be added to express more specific expectations.
|
116
157
|
|
117
158
|
A **Be the answer** matcher:
|
118
159
|
|
119
160
|
```ruby
|
120
161
|
module Matchi
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
162
|
+
class BeTheAnswer
|
163
|
+
def expected
|
164
|
+
42
|
165
|
+
end
|
166
|
+
|
167
|
+
def matches?
|
168
|
+
expected.equal?(yield)
|
126
169
|
end
|
127
170
|
end
|
128
171
|
end
|
129
172
|
|
130
|
-
|
131
|
-
|
173
|
+
matcher = Matchi::BeTheAnswer.new
|
174
|
+
|
175
|
+
matcher.expected # => 42
|
176
|
+
matcher.matches? { 42 } # => true
|
132
177
|
```
|
133
178
|
|
134
179
|
A **Be prime** matcher:
|
@@ -137,60 +182,42 @@ A **Be prime** matcher:
|
|
137
182
|
require "prime"
|
138
183
|
|
139
184
|
module Matchi
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
185
|
+
class BePrime
|
186
|
+
attr_reader :expected
|
187
|
+
|
188
|
+
def matches?
|
189
|
+
Prime.prime?(yield)
|
145
190
|
end
|
146
191
|
end
|
147
192
|
end
|
148
193
|
|
149
|
-
|
150
|
-
|
194
|
+
matcher = Matchi::BePrime.new
|
195
|
+
|
196
|
+
matcher.expected # => nil
|
197
|
+
matcher.matches? { 42 } # => false
|
151
198
|
```
|
152
199
|
|
153
200
|
A **Start with** matcher:
|
154
201
|
|
155
202
|
```ruby
|
156
203
|
module Matchi
|
157
|
-
|
158
|
-
|
159
|
-
def initialize(expected)
|
160
|
-
super()
|
161
|
-
@expected = expected
|
162
|
-
end
|
163
|
-
|
164
|
-
def matches?
|
165
|
-
Regexp.new(/\A#{expected}/).match?(yield)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
204
|
+
class StartWith
|
205
|
+
attr_reader :expected
|
170
206
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
### Helper methods
|
176
|
-
|
177
|
-
For convenience, it is possible to instantiate a matcher with a method rather than with its class.
|
178
|
-
To do so, the `Helper` module can be included like this:
|
179
|
-
|
180
|
-
```ruby
|
181
|
-
require "matchi/helper"
|
207
|
+
def initialize(expected)
|
208
|
+
@expected = expected
|
209
|
+
end
|
182
210
|
|
183
|
-
|
184
|
-
|
211
|
+
def matches?
|
212
|
+
Regexp.new(/\A#{expected}/).match?(yield)
|
213
|
+
end
|
214
|
+
end
|
185
215
|
end
|
186
|
-
```
|
187
216
|
|
188
|
-
|
217
|
+
matcher = Matchi::StartWith.new("foo")
|
189
218
|
|
190
|
-
|
191
|
-
matcher
|
192
|
-
matcher.equal(42).matches? { 44 } # => false
|
193
|
-
matcher.be_an_instance_of(:String).matches? { "안녕하세요" } # => true
|
219
|
+
matcher.expected # => "foo"
|
220
|
+
matcher.matches? { "foobar" } # => true
|
194
221
|
```
|
195
222
|
|
196
223
|
## Contact
|
@@ -204,7 +231,7 @@ __Matchi__ follows [Semantic Versioning 2.0](https://semver.org/).
|
|
204
231
|
|
205
232
|
## License
|
206
233
|
|
207
|
-
The [gem](https://rubygems.org/gems/matchi) is available as open source under the terms of the [MIT License](https://
|
234
|
+
The [gem](https://rubygems.org/gems/matchi) is available as open source under the terms of the [MIT License](https://github.com/fixrb/matchi/raw/main/LICENSE.md).
|
208
235
|
|
209
236
|
***
|
210
237
|
|
data/lib/matchi.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
3
|
+
# A collection of damn simple expectation matchers.
|
4
4
|
#
|
5
5
|
# @api public
|
6
6
|
module Matchi
|
7
7
|
end
|
8
8
|
|
9
|
-
|
9
|
+
Dir[File.join(File.dirname(__FILE__), "matchi", "*.rb")].each do |fname|
|
10
|
+
require_relative fname
|
11
|
+
end
|
data/lib/matchi/be.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Matchi
|
4
|
+
# *Identity* matcher.
|
5
|
+
class Be
|
6
|
+
# @return [#equal?] The expected identical object.
|
7
|
+
attr_reader :expected
|
8
|
+
|
9
|
+
# Initialize the matcher with an object.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# require "matchi/be"
|
13
|
+
#
|
14
|
+
# Matchi::Be.new(:foo)
|
15
|
+
#
|
16
|
+
# @param expected [#equal?] The expected identical object.
|
17
|
+
def initialize(expected)
|
18
|
+
@expected = expected
|
19
|
+
end
|
20
|
+
|
21
|
+
# Boolean comparison between the actual value and the expected value.
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# require "matchi/be"
|
25
|
+
#
|
26
|
+
# matcher = Matchi::Be.new(:foo)
|
27
|
+
#
|
28
|
+
# matcher.expected # => :foo
|
29
|
+
# matcher.matches? { :foo } # => true
|
30
|
+
#
|
31
|
+
# @yieldreturn [#object_id] The actual value to compare to the expected
|
32
|
+
# one.
|
33
|
+
#
|
34
|
+
# @return [Boolean] Comparison between actual and expected values.
|
35
|
+
def matches?
|
36
|
+
expected.equal?(yield)
|
37
|
+
end
|
38
|
+
|
39
|
+
# A string containing a human-readable representation of the matcher.
|
40
|
+
def inspect
|
41
|
+
"#{self.class}(#{expected.inspect})"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns a string representing the matcher.
|
45
|
+
def to_s
|
46
|
+
"be #{expected.inspect}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Matchi
|
4
|
+
# *Type/class* matcher.
|
5
|
+
class BeAnInstanceOf
|
6
|
+
# @return [String] The expected class name.
|
7
|
+
attr_reader :expected
|
8
|
+
|
9
|
+
# Initialize the matcher with (the name of) a class or module.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# require "matchi/be_an_instance_of"
|
13
|
+
#
|
14
|
+
# Matchi::BeAnInstanceOf.new(String)
|
15
|
+
#
|
16
|
+
# @param expected [Class, #to_s] The expected class name.
|
17
|
+
def initialize(expected)
|
18
|
+
@expected = String(expected)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Boolean comparison between the class of the actual value and the
|
22
|
+
# expected class.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# require "matchi/be_an_instance_of"
|
26
|
+
#
|
27
|
+
# matcher = Matchi::BeAnInstanceOf.new(String)
|
28
|
+
#
|
29
|
+
# matcher.expected # => "String"
|
30
|
+
# matcher.matches? { "foo" } # => true
|
31
|
+
#
|
32
|
+
# @yieldreturn [#class] the actual value to compare to the expected one.
|
33
|
+
#
|
34
|
+
# @return [Boolean] Comparison between actual and expected values.
|
35
|
+
def matches?
|
36
|
+
self.class.const_get(expected).equal?(yield.class)
|
37
|
+
end
|
38
|
+
|
39
|
+
# A string containing a human-readable representation of the matcher.
|
40
|
+
def inspect
|
41
|
+
"#{self.class}(#{expected})"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns a string representing the matcher.
|
45
|
+
def to_s
|
46
|
+
"be an instance of #{expected}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative File.join("change", "by_at_least")
|
4
|
+
require_relative File.join("change", "by_at_most")
|
5
|
+
require_relative File.join("change", "by")
|
6
|
+
require_relative File.join("change", "from")
|
7
|
+
require_relative File.join("change", "to")
|
8
|
+
|
9
|
+
module Matchi
|
10
|
+
# Wraps the target of a change matcher.
|
11
|
+
class Change
|
12
|
+
# Initialize a wrapper of the change matcher with an object and the name of
|
13
|
+
# one of its methods.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# require "matchi/change"
|
17
|
+
#
|
18
|
+
# Matchi::Change.new("foo", :to_s)
|
19
|
+
#
|
20
|
+
# @param object [#object_id] An object.
|
21
|
+
# @param method [Symbol] The name of a method.
|
22
|
+
# @param args [Array] A list of arguments.
|
23
|
+
# @param kwargs [Hash] A list of keyword arguments.
|
24
|
+
def initialize(object, method, *args, **kwargs, &block)
|
25
|
+
@state = -> { object.send(method, *args, **kwargs, &block) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Specifies a minimum delta of the expected change.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# require "matchi/change"
|
32
|
+
#
|
33
|
+
# object = []
|
34
|
+
#
|
35
|
+
# change_wrapper = Matchi::Change.new(object, :length)
|
36
|
+
# change_wrapper.by_at_least(1)
|
37
|
+
#
|
38
|
+
# @param minimum_delta [#object_id] The minimum delta of the expected change.
|
39
|
+
#
|
40
|
+
# @return [#matches?] A *change by at least* matcher.
|
41
|
+
def by_at_least(minimum_delta)
|
42
|
+
ByAtLeast.new(minimum_delta, &@state)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Specifies a maximum delta of the expected change.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# require "matchi/change"
|
49
|
+
#
|
50
|
+
# object = []
|
51
|
+
#
|
52
|
+
# change_wrapper = Matchi::Change.new(object, :length)
|
53
|
+
# change_wrapper.by_at_most(1)
|
54
|
+
#
|
55
|
+
# @param maximum_delta [#object_id] The maximum delta of the expected change.
|
56
|
+
#
|
57
|
+
# @return [#matches?] A *change by at most* matcher.
|
58
|
+
def by_at_most(maximum_delta)
|
59
|
+
ByAtMost.new(maximum_delta, &@state)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Specifies the delta of the expected change.
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# require "matchi/change"
|
66
|
+
#
|
67
|
+
# object = []
|
68
|
+
#
|
69
|
+
# change_wrapper = Matchi::Change.new(object, :length)
|
70
|
+
# change_wrapper.by(1)
|
71
|
+
#
|
72
|
+
# @param delta [#object_id] The delta of the expected change.
|
73
|
+
#
|
74
|
+
# @return [#matches?] A *change by* matcher.
|
75
|
+
def by(delta)
|
76
|
+
By.new(delta, &@state)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Specifies the original value.
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# require "matchi/change"
|
83
|
+
#
|
84
|
+
# change_wrapper = Matchi::Change.new("foo", :to_s)
|
85
|
+
# change_wrapper.from("foo")
|
86
|
+
#
|
87
|
+
# @param old_value [#object_id] The original value.
|
88
|
+
#
|
89
|
+
# @return [#matches?] A *change from* wrapper.
|
90
|
+
def from(old_value)
|
91
|
+
From.new(old_value, &@state)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Specifies the new value to expect.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# require "matchi/change"
|
98
|
+
#
|
99
|
+
# change_wrapper = Matchi::Change.new("foo", :to_s)
|
100
|
+
# change_wrapper.to("FOO")
|
101
|
+
#
|
102
|
+
# @param new_value [#object_id] The new value to expect.
|
103
|
+
#
|
104
|
+
# @return [#matches?] A *change to* matcher.
|
105
|
+
def to(new_value)
|
106
|
+
To.new(new_value, &@state)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|