matchi 4.1.1 → 4.2.0
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 +148 -219
- data/lib/matchi/be.rb +58 -16
- data/lib/matchi/be_a_kind_of.rb +87 -34
- data/lib/matchi/be_an_instance_of.rb +74 -62
- data/lib/matchi/be_within.rb +125 -14
- data/lib/matchi/change/by.rb +99 -22
- data/lib/matchi/change/by_at_least.rb +118 -21
- data/lib/matchi/change/by_at_most.rb +132 -22
- data/lib/matchi/change/from/to.rb +92 -25
- data/lib/matchi/change/from.rb +31 -1
- data/lib/matchi/change/to.rb +72 -23
- data/lib/matchi/change.rb +119 -45
- data/lib/matchi/eq.rb +58 -16
- data/lib/matchi/match.rb +56 -16
- data/lib/matchi/predicate.rb +122 -33
- data/lib/matchi/raise_exception.rb +89 -22
- data/lib/matchi/satisfy.rb +87 -16
- data/lib/matchi.rb +51 -297
- metadata +12 -5
- data/lib/matchi/be_within/of.rb +0 -51
@@ -1,16 +1,59 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Matchi
|
4
|
-
#
|
4
|
+
# Exception matcher that verifies if a block of code raises a specific exception.
|
5
|
+
#
|
6
|
+
# This matcher ensures that code raises an expected exception by executing it within
|
7
|
+
# a controlled environment. It supports matching against specific exception classes
|
8
|
+
# and their subclasses, providing a reliable way to test error handling.
|
9
|
+
#
|
10
|
+
# @example Basic usage with standard exceptions
|
11
|
+
# matcher = Matchi::RaiseException.new(ArgumentError)
|
12
|
+
# matcher.match? { raise ArgumentError } # => true
|
13
|
+
# matcher.match? { raise RuntimeError } # => false
|
14
|
+
# matcher.match? { "no error" } # => false
|
15
|
+
#
|
16
|
+
# @example Working with inheritance hierarchy
|
17
|
+
# class CustomError < StandardError; end
|
18
|
+
# class SpecificError < CustomError; end
|
19
|
+
#
|
20
|
+
# matcher = Matchi::RaiseException.new(CustomError)
|
21
|
+
# matcher.match? { raise CustomError } # => true
|
22
|
+
# matcher.match? { raise SpecificError } # => true
|
23
|
+
# matcher.match? { raise StandardError } # => false
|
24
|
+
#
|
25
|
+
# @example Using string class names
|
26
|
+
# matcher = Matchi::RaiseException.new("ArgumentError")
|
27
|
+
# matcher.match? { raise ArgumentError } # => true
|
28
|
+
#
|
29
|
+
# @example With custom exception hierarchies
|
30
|
+
# module Api
|
31
|
+
# class Error < StandardError; end
|
32
|
+
# class AuthenticationError < Error; end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# matcher = Matchi::RaiseException.new("Api::Error")
|
36
|
+
# matcher.match? { raise Api::AuthenticationError } # => true
|
37
|
+
#
|
38
|
+
# @see https://ruby-doc.org/core/Exception.html
|
39
|
+
# @see https://ruby-doc.org/core/StandardError.html
|
5
40
|
class RaiseException
|
6
|
-
# Initialize the matcher with
|
41
|
+
# Initialize the matcher with an exception class.
|
7
42
|
#
|
8
|
-
# @
|
9
|
-
#
|
43
|
+
# @api public
|
44
|
+
#
|
45
|
+
# @param expected [Exception, #to_s] The expected exception class or its name
|
46
|
+
# Can be provided as a Class, String, or Symbol
|
10
47
|
#
|
11
|
-
#
|
48
|
+
# @raise [ArgumentError] if the class name doesn't start with an uppercase letter
|
12
49
|
#
|
13
|
-
# @
|
50
|
+
# @return [RaiseException] a new instance of the matcher
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# RaiseException.new(ArgumentError) # Using class
|
54
|
+
# RaiseException.new("ArgumentError") # Using string
|
55
|
+
# RaiseException.new(:ArgumentError) # Using symbol
|
56
|
+
# RaiseException.new("Api::CustomError") # Using namespaced class
|
14
57
|
def initialize(expected)
|
15
58
|
@expected = String(expected)
|
16
59
|
return if /\A[A-Z]/.match?(@expected)
|
@@ -19,18 +62,34 @@ module Matchi
|
|
19
62
|
"expected must start with an uppercase letter (got: #{@expected})"
|
20
63
|
end
|
21
64
|
|
22
|
-
#
|
65
|
+
# Checks if the yielded block raises the expected exception.
|
23
66
|
#
|
24
|
-
#
|
25
|
-
#
|
67
|
+
# This method executes the provided block within a begin/rescue clause and verifies
|
68
|
+
# that it raises an exception of the expected type. It also handles inheritance,
|
69
|
+
# allowing subclasses of the expected exception to match.
|
70
|
+
#
|
71
|
+
# @api public
|
72
|
+
#
|
73
|
+
# @yield [] Block that should raise an exception
|
74
|
+
# @yieldreturn [Object] The result of the block (if it doesn't raise)
|
26
75
|
#
|
27
|
-
#
|
28
|
-
# matcher.match? { Boom } # => true
|
76
|
+
# @return [Boolean] true if the block raises the expected exception
|
29
77
|
#
|
30
|
-
# @
|
31
|
-
#
|
78
|
+
# @raise [ArgumentError] if no block is provided
|
79
|
+
# @raise [ArgumentError] if expected exception class doesn't inherit from Exception
|
80
|
+
# @raise [NameError] if the expected exception class cannot be found
|
32
81
|
#
|
33
|
-
# @
|
82
|
+
# @example Standard usage
|
83
|
+
# matcher = RaiseException.new(ArgumentError)
|
84
|
+
# matcher.match? { raise ArgumentError } # => true
|
85
|
+
#
|
86
|
+
# @example With inheritance
|
87
|
+
# matcher = RaiseException.new(StandardError)
|
88
|
+
# matcher.match? { raise ArgumentError } # => true
|
89
|
+
#
|
90
|
+
# @example Without exception
|
91
|
+
# matcher = RaiseException.new(StandardError)
|
92
|
+
# matcher.match? { "no error" } # => false
|
34
93
|
def match?
|
35
94
|
raise ::ArgumentError, "a block must be provided" unless block_given?
|
36
95
|
|
@@ -40,25 +99,33 @@ module Matchi
|
|
40
99
|
begin
|
41
100
|
yield
|
42
101
|
false
|
43
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
44
|
-
e.class <= klass
|
102
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
103
|
+
e.class <= klass
|
45
104
|
end
|
46
105
|
end
|
47
106
|
|
48
|
-
# Returns a
|
107
|
+
# Returns a human-readable description of the matcher.
|
108
|
+
#
|
109
|
+
# @api public
|
110
|
+
#
|
111
|
+
# @return [String] A string describing what this matcher verifies
|
49
112
|
#
|
50
|
-
# @
|
113
|
+
# @example
|
114
|
+
# RaiseException.new(ArgumentError).to_s # => "raise exception ArgumentError"
|
51
115
|
def to_s
|
52
116
|
"raise exception #{@expected}"
|
53
117
|
end
|
54
118
|
|
55
119
|
private
|
56
120
|
|
57
|
-
# Resolves the expected class name to an actual
|
58
|
-
#
|
121
|
+
# Resolves the expected class name to an actual Exception class.
|
122
|
+
#
|
123
|
+
# @api private
|
124
|
+
#
|
125
|
+
# @return [Class] The resolved exception class
|
126
|
+
# @raise [NameError] If the class name cannot be resolved to an actual class
|
59
127
|
#
|
60
|
-
# @
|
61
|
-
# @raise [NameError] if the class doesn't exist
|
128
|
+
# @note This method handles both string and symbol class names through constant resolution
|
62
129
|
def expected_class
|
63
130
|
::Object.const_get(@expected)
|
64
131
|
end
|
data/lib/matchi/satisfy.rb
CHANGED
@@ -1,43 +1,114 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Matchi
|
4
|
-
#
|
4
|
+
# Custom predicate matcher that validates values against an arbitrary condition.
|
5
|
+
#
|
6
|
+
# This matcher provides a flexible way to test values against custom conditions
|
7
|
+
# defined in a block. Unlike specific matchers that test for predetermined
|
8
|
+
# conditions, Satisfy allows you to define any custom validation logic at runtime.
|
9
|
+
# This makes it particularly useful for complex or composite conditions that
|
10
|
+
# aren't covered by other matchers.
|
11
|
+
#
|
12
|
+
# @example Basic numeric validation
|
13
|
+
# matcher = Matchi::Satisfy.new { |n| n.positive? && n.even? }
|
14
|
+
# matcher.match? { 2 } # => true
|
15
|
+
# matcher.match? { -2 } # => false
|
16
|
+
# matcher.match? { 3 } # => false
|
17
|
+
#
|
18
|
+
# @example String pattern validation
|
19
|
+
# matcher = Matchi::Satisfy.new { |s| s.start_with?("test") && s.length < 10 }
|
20
|
+
# matcher.match? { "test_123" } # => true
|
21
|
+
# matcher.match? { "test_12345" } # => false
|
22
|
+
# matcher.match? { "other" } # => false
|
23
|
+
#
|
24
|
+
# @example Complex object validation
|
25
|
+
# class User
|
26
|
+
# attr_reader :age, :name
|
27
|
+
# def initialize(name, age)
|
28
|
+
# @name, @age = name, age
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# matcher = Matchi::Satisfy.new { |user|
|
33
|
+
# user.age >= 18 && user.name.length >= 2
|
34
|
+
# }
|
35
|
+
# matcher.match? { User.new("Alice", 25) } # => true
|
36
|
+
# matcher.match? { User.new("B", 20) } # => false
|
37
|
+
#
|
38
|
+
# @example Using with collections
|
39
|
+
# matcher = Matchi::Satisfy.new { |arr|
|
40
|
+
# arr.all? { |x| x.is_a?(Integer) } && arr.sum.even?
|
41
|
+
# }
|
42
|
+
# matcher.match? { [2, 4, 6] } # => true
|
43
|
+
# matcher.match? { [1, 3, 5] } # => false
|
44
|
+
# matcher.match? { [1, "2", 3] } # => false
|
45
|
+
#
|
46
|
+
# @see https://ruby-doc.org/core/Proc.html
|
47
|
+
# @see Matchi::Predicate For testing existing predicate methods
|
5
48
|
class Satisfy
|
6
|
-
# Initialize the matcher with a block.
|
49
|
+
# Initialize the matcher with a validation block.
|
7
50
|
#
|
8
|
-
# @
|
9
|
-
#
|
51
|
+
# @api public
|
52
|
+
#
|
53
|
+
# @yield [Object] Block that defines the validation condition
|
54
|
+
# @yieldparam value The value to validate
|
55
|
+
# @yieldreturn [Boolean] true if the value meets the condition
|
56
|
+
#
|
57
|
+
# @raise [ArgumentError] if no block is provided
|
10
58
|
#
|
11
|
-
#
|
59
|
+
# @return [Satisfy] a new instance of the matcher
|
12
60
|
#
|
13
|
-
# @
|
61
|
+
# @example Simple numeric validation
|
62
|
+
# Satisfy.new { |n| n > 0 }
|
63
|
+
#
|
64
|
+
# @example Complex condition
|
65
|
+
# Satisfy.new { |obj|
|
66
|
+
# obj.respond_to?(:length) && obj.length.between?(2, 10)
|
67
|
+
# }
|
14
68
|
def initialize(&block)
|
15
69
|
raise ::ArgumentError, "a block must be provided" unless block_given?
|
16
70
|
|
17
71
|
@expected = block
|
18
72
|
end
|
19
73
|
|
20
|
-
#
|
74
|
+
# Checks if the yielded value satisfies the validation block.
|
21
75
|
#
|
22
|
-
#
|
23
|
-
#
|
76
|
+
# This method passes the value returned by the provided block to the
|
77
|
+
# validation block defined at initialization. The matcher succeeds if
|
78
|
+
# the validation block returns a truthy value.
|
79
|
+
#
|
80
|
+
# @api public
|
24
81
|
#
|
25
|
-
#
|
26
|
-
#
|
82
|
+
# @yield [] Block that returns the value to validate
|
83
|
+
# @yieldreturn [Object] The value to check against the condition
|
27
84
|
#
|
28
|
-
# @
|
29
|
-
# one.
|
85
|
+
# @return [Boolean] true if the value satisfies the condition
|
30
86
|
#
|
31
|
-
# @
|
87
|
+
# @raise [ArgumentError] if no block is provided
|
88
|
+
#
|
89
|
+
# @example Using with direct values
|
90
|
+
# matcher = Satisfy.new { |n| n.positive? }
|
91
|
+
# matcher.match? { 42 } # => true
|
92
|
+
# matcher.match? { -1 } # => false
|
93
|
+
#
|
94
|
+
# @example Using with computed values
|
95
|
+
# matcher = Satisfy.new { |s| s.length.even? }
|
96
|
+
# matcher.match? { "test".upcase } # => true
|
97
|
+
# matcher.match? { "a" * 3 } # => false
|
32
98
|
def match?
|
33
99
|
raise ::ArgumentError, "a block must be provided" unless block_given?
|
34
100
|
|
35
101
|
@expected.call(yield)
|
36
102
|
end
|
37
103
|
|
38
|
-
# Returns a
|
104
|
+
# Returns a human-readable description of the matcher.
|
105
|
+
#
|
106
|
+
# @api public
|
39
107
|
#
|
40
|
-
# @return [String]
|
108
|
+
# @return [String] A string describing what this matcher verifies
|
109
|
+
#
|
110
|
+
# @example
|
111
|
+
# Satisfy.new { |n| n > 0 }.to_s # => "satisfy &block"
|
41
112
|
def to_s
|
42
113
|
"satisfy &block"
|
43
114
|
end
|
data/lib/matchi.rb
CHANGED
@@ -1,311 +1,65 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
3
|
+
# Main namespace for the Matchi gem, providing a collection of expectation matchers.
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Matchi is a framework-agnostic Ruby library that offers a comprehensive set of
|
6
|
+
# matchers for testing and verification purposes. Each matcher follows a consistent
|
7
|
+
# interface pattern, making them easy to use and extend.
|
6
8
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# matcher.match? { "foo" } # => true
|
11
|
-
# ```
|
9
|
+
# @example Basic usage with equality matcher
|
10
|
+
# Matchi::Eq.new("hello").match? { "hello" } # => true
|
11
|
+
# Matchi::Eq.new("hello").match? { "world" } # => false
|
12
12
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# class MyTestFramework
|
17
|
-
# include Matchi
|
13
|
+
# @example Type checking with inheritance awareness
|
14
|
+
# Matchi::BeAKindOf.new(Numeric).match? { 42 } # => true
|
15
|
+
# Matchi::BeAKindOf.new(String).match? { 42 } # => false
|
18
16
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# matcher.match? { "foo" } # => true
|
17
|
+
# @example Verifying state changes
|
18
|
+
# array = []
|
19
|
+
# Matchi::Change.new(array, :length).by(2).match? { array.push(1, 2) } # => true
|
23
20
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# end
|
27
|
-
# end
|
21
|
+
# @example Pattern matching with regular expressions
|
22
|
+
# Matchi::Match.new(/^test/).match? { "test_string" } # => true
|
28
23
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
24
|
+
# Each matcher in the Matchi ecosystem implements a consistent interface:
|
25
|
+
# - An initializer that sets up the expected values or conditions
|
26
|
+
# - A #match? method that takes a block and returns a boolean
|
27
|
+
# - An optional #to_s method that provides a human-readable description
|
32
28
|
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
29
|
+
# @example Creating a custom matcher
|
30
|
+
# module Matchi
|
31
|
+
# class BePositive
|
32
|
+
# def match?
|
33
|
+
# raise ArgumentError, "a block must be provided" unless block_given?
|
34
|
+
# yield.positive?
|
35
|
+
# end
|
38
36
|
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
37
|
+
# def to_s
|
38
|
+
# "be positive"
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# end
|
42
42
|
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
# - be_* & have_* : Dynamic predicate matching
|
54
|
-
#
|
55
|
-
# @api public
|
43
|
+
# @see Matchi::Be
|
44
|
+
# @see Matchi::BeAKindOf
|
45
|
+
# @see Matchi::BeAnInstanceOf
|
46
|
+
# @see Matchi::BeWithin
|
47
|
+
# @see Matchi::Change
|
48
|
+
# @see Matchi::Eq
|
49
|
+
# @see Matchi::Match
|
50
|
+
# @see Matchi::Predicate
|
51
|
+
# @see Matchi::RaiseException
|
52
|
+
# @see Matchi::Satisfy
|
56
53
|
module Matchi
|
57
|
-
# Equivalence matcher
|
58
|
-
#
|
59
|
-
# @example
|
60
|
-
# matcher = eq("foo")
|
61
|
-
# matcher.match? { "foo" } # => true
|
62
|
-
# matcher.match? { "bar" } # => false
|
63
|
-
#
|
64
|
-
# @param expected [#eql?] An expected equivalent object.
|
65
|
-
#
|
66
|
-
# @return [#match?] An equivalence matcher.
|
67
|
-
#
|
68
|
-
# @api public
|
69
|
-
def eq(expected)
|
70
|
-
::Matchi::Eq.new(expected)
|
71
|
-
end
|
72
|
-
|
73
|
-
alias eql eq
|
74
|
-
|
75
|
-
# Identity matcher
|
76
|
-
#
|
77
|
-
# @example
|
78
|
-
# object = "foo"
|
79
|
-
# matcher = be(object)
|
80
|
-
# matcher.match? { object } # => true
|
81
|
-
# matcher.match? { "foo" } # => false
|
82
|
-
#
|
83
|
-
# @param expected [#equal?] The expected identical object.
|
84
|
-
#
|
85
|
-
# @return [#match?] An identity matcher.
|
86
|
-
#
|
87
|
-
# @api public
|
88
|
-
def be(expected)
|
89
|
-
::Matchi::Be.new(expected)
|
90
|
-
end
|
91
|
-
|
92
|
-
alias equal be
|
93
|
-
|
94
|
-
# Comparisons matcher
|
95
|
-
#
|
96
|
-
# @example
|
97
|
-
# matcher = be_within(1).of(41)
|
98
|
-
# matcher.match? { 42 } # => true
|
99
|
-
# matcher.match? { 43 } # => false
|
100
|
-
#
|
101
|
-
# @param delta [Numeric] A numeric value.
|
102
|
-
#
|
103
|
-
# @return [#match?] A comparison matcher.
|
104
|
-
#
|
105
|
-
# @api public
|
106
|
-
def be_within(delta)
|
107
|
-
::Matchi::BeWithin.new(delta)
|
108
|
-
end
|
109
|
-
|
110
|
-
# Regular expressions matcher
|
111
|
-
#
|
112
|
-
# @example
|
113
|
-
# matcher = match(/^foo$/)
|
114
|
-
# matcher.match? { "foo" } # => true
|
115
|
-
# matcher.match? { "bar" } # => false
|
116
|
-
#
|
117
|
-
# @param expected [#match] A regular expression.
|
118
|
-
#
|
119
|
-
# @return [#match?] A regular expression matcher.
|
120
|
-
#
|
121
|
-
# @api public
|
122
|
-
def match(expected)
|
123
|
-
::Matchi::Match.new(expected)
|
124
|
-
end
|
125
|
-
|
126
|
-
# Expecting errors matcher
|
127
|
-
#
|
128
|
-
# @example
|
129
|
-
# matcher = raise_exception(NameError)
|
130
|
-
# matcher.match? { Boom } # => true
|
131
|
-
# matcher.match? { true } # => false
|
132
|
-
#
|
133
|
-
# @param expected [Exception, #to_s] The expected exception name.
|
134
|
-
#
|
135
|
-
# @return [#match?] An error matcher.
|
136
|
-
#
|
137
|
-
# @api public
|
138
|
-
def raise_exception(expected)
|
139
|
-
::Matchi::RaiseException.new(expected)
|
140
|
-
end
|
141
|
-
|
142
|
-
# True matcher
|
143
|
-
#
|
144
|
-
# @example
|
145
|
-
# matcher = be_true
|
146
|
-
# matcher.match? { true } # => true
|
147
|
-
# matcher.match? { false } # => false
|
148
|
-
# matcher.match? { nil } # => false
|
149
|
-
# matcher.match? { 4 } # => false
|
150
|
-
#
|
151
|
-
# @return [#match?] A `true` matcher.
|
152
|
-
#
|
153
|
-
# @api public
|
154
|
-
def be_true
|
155
|
-
be(true)
|
156
|
-
end
|
157
|
-
|
158
|
-
# False matcher
|
159
|
-
#
|
160
|
-
# @example
|
161
|
-
# matcher = be_false
|
162
|
-
# matcher.match? { false } # => true
|
163
|
-
# matcher.match? { true } # => false
|
164
|
-
# matcher.match? { nil } # => false
|
165
|
-
# matcher.match? { 4 } # => false
|
166
|
-
#
|
167
|
-
# @return [#match?] A `false` matcher.
|
168
|
-
#
|
169
|
-
# @api public
|
170
|
-
def be_false
|
171
|
-
be(false)
|
172
|
-
end
|
173
|
-
|
174
|
-
# Nil matcher
|
175
|
-
#
|
176
|
-
# @example
|
177
|
-
# matcher = be_nil
|
178
|
-
# matcher.match? { nil } # => true
|
179
|
-
# matcher.match? { false } # => false
|
180
|
-
# matcher.match? { true } # => false
|
181
|
-
# matcher.match? { 4 } # => false
|
182
|
-
#
|
183
|
-
# @return [#match?] A `nil` matcher.
|
184
|
-
#
|
185
|
-
# @api public
|
186
|
-
def be_nil
|
187
|
-
be(nil)
|
188
|
-
end
|
189
|
-
|
190
|
-
# Type/class matcher
|
191
|
-
#
|
192
|
-
# Verifies exact class matching (no inheritance).
|
193
|
-
#
|
194
|
-
# @example
|
195
|
-
# matcher = be_an_instance_of(String)
|
196
|
-
# matcher.match? { "foo" } # => true
|
197
|
-
# matcher.match? { 4 } # => false
|
198
|
-
#
|
199
|
-
# @param expected [Class, #to_s] The expected class name.
|
200
|
-
#
|
201
|
-
# @return [#match?] A type/class matcher.
|
202
|
-
#
|
203
|
-
# @api public
|
204
|
-
def be_an_instance_of(expected)
|
205
|
-
::Matchi::BeAnInstanceOf.new(expected)
|
206
|
-
end
|
207
|
-
|
208
|
-
# Type/class matcher
|
209
|
-
#
|
210
|
-
# Verifies class inheritance and module inclusion.
|
211
|
-
#
|
212
|
-
# @example
|
213
|
-
# matcher = be_a_kind_of(Numeric)
|
214
|
-
# matcher.match? { 42 } # => true (Integer inherits from Numeric)
|
215
|
-
# matcher.match? { 42.0 } # => true (Float inherits from Numeric)
|
216
|
-
#
|
217
|
-
# @param expected [Class, #to_s] The expected class name.
|
218
|
-
#
|
219
|
-
# @return [#match?] A type/class matcher.
|
220
|
-
#
|
221
|
-
# @api public
|
222
|
-
def be_a_kind_of(expected)
|
223
|
-
::Matchi::BeAKindOf.new(expected)
|
224
|
-
end
|
225
|
-
|
226
|
-
# Change matcher
|
227
|
-
#
|
228
|
-
# @example
|
229
|
-
# object = []
|
230
|
-
# matcher = change(object, :length).by(1)
|
231
|
-
# matcher.match? { object << 1 } # => true
|
232
|
-
#
|
233
|
-
# object = []
|
234
|
-
# matcher = change(object, :length).by_at_least(1)
|
235
|
-
# matcher.match? { object << 1 } # => true
|
236
|
-
#
|
237
|
-
# object = []
|
238
|
-
# matcher = change(object, :length).by_at_most(1)
|
239
|
-
# matcher.match? { object << 1 } # => true
|
240
|
-
#
|
241
|
-
# object = "foo"
|
242
|
-
# matcher = change(object, :to_s).from("foo").to("FOO")
|
243
|
-
# matcher.match? { object.upcase! } # => true
|
244
|
-
#
|
245
|
-
# object = "foo"
|
246
|
-
# matcher = change(object, :to_s).to("FOO")
|
247
|
-
# matcher.match? { object.upcase! } # => true
|
248
|
-
#
|
249
|
-
# @param object [#object_id] An object.
|
250
|
-
# @param method [Symbol] The name of a method.
|
251
|
-
#
|
252
|
-
# @return [#match?] A change matcher.
|
253
|
-
#
|
254
|
-
# @api public
|
255
|
-
def change(object, method, ...)
|
256
|
-
::Matchi::Change.new(object, method, ...)
|
257
|
-
end
|
258
|
-
|
259
|
-
# Satisfy matcher
|
260
|
-
#
|
261
|
-
# @example
|
262
|
-
# matcher = satisfy { |value| value == 42 }
|
263
|
-
# matcher.match? { 42 } # => true
|
264
|
-
#
|
265
|
-
# @yield [value] A block that defines the satisfaction criteria
|
266
|
-
# @yieldparam value The value to test
|
267
|
-
# @yieldreturn [Boolean] true if the value satisfies the criteria
|
268
|
-
#
|
269
|
-
# @return [#match?] A satisfy matcher.
|
270
|
-
#
|
271
|
-
# @api public
|
272
|
-
def satisfy(&)
|
273
|
-
::Matchi::Satisfy.new(&)
|
274
|
-
end
|
275
|
-
|
276
|
-
private
|
277
|
-
|
278
|
-
# Predicate matcher, or default method missing behavior.
|
279
|
-
#
|
280
|
-
# @example Empty predicate matcher
|
281
|
-
# matcher = be_empty
|
282
|
-
# matcher.match? { [] } # => true
|
283
|
-
# matcher.match? { [4] } # => false
|
284
|
-
def method_missing(name, ...)
|
285
|
-
return super unless predicate_matcher_name?(name)
|
286
|
-
|
287
|
-
::Matchi::Predicate.new(name, ...)
|
288
|
-
end
|
289
|
-
|
290
|
-
# :nocov:
|
291
|
-
|
292
|
-
# Hook method to return whether the obj can respond to id method or not.
|
293
|
-
def respond_to_missing?(name, include_private = false)
|
294
|
-
predicate_matcher_name?(name) || super
|
295
|
-
end
|
296
|
-
|
297
|
-
# :nocov:
|
298
|
-
|
299
|
-
# Predicate matcher name detector.
|
300
|
-
#
|
301
|
-
# @param name [Array, Symbol] The name of a potential predicate matcher.
|
302
|
-
#
|
303
|
-
# @return [Boolean] Indicates if it is a predicate matcher name or not.
|
304
|
-
def predicate_matcher_name?(name)
|
305
|
-
name.start_with?("be_", "have_") && !name.end_with?("!", "?")
|
306
|
-
end
|
307
54
|
end
|
308
55
|
|
309
|
-
|
310
|
-
|
311
|
-
|
56
|
+
require "matchi/be"
|
57
|
+
require "matchi/be_a_kind_of"
|
58
|
+
require "matchi/be_an_instance_of"
|
59
|
+
require "matchi/be_within"
|
60
|
+
require "matchi/change"
|
61
|
+
require "matchi/eq"
|
62
|
+
require "matchi/match"
|
63
|
+
require "matchi/predicate"
|
64
|
+
require "matchi/raise_exception"
|
65
|
+
require "matchi/satisfy"
|
metadata
CHANGED
@@ -1,16 +1,24 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: matchi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description: 'Matchi is a framework-agnostic Ruby library that provides a comprehensive
|
14
|
+
set of expectation matchers for elegant and secure testing. Its design focuses on
|
15
|
+
simplicity, security, and extensibility, making it easy to integrate with any testing
|
16
|
+
framework. The library offers a rich collection of built-in matchers for common
|
17
|
+
testing scenarios while maintaining a clear, consistent API that follows Ruby best
|
18
|
+
practices. With minimal setup required and support for custom matchers, Matchi enables
|
19
|
+
developers to write more reliable and maintainable tests.
|
20
|
+
|
21
|
+
'
|
14
22
|
email: contact@cyril.email
|
15
23
|
executables: []
|
16
24
|
extensions: []
|
@@ -23,7 +31,6 @@ files:
|
|
23
31
|
- lib/matchi/be_a_kind_of.rb
|
24
32
|
- lib/matchi/be_an_instance_of.rb
|
25
33
|
- lib/matchi/be_within.rb
|
26
|
-
- lib/matchi/be_within/of.rb
|
27
34
|
- lib/matchi/change.rb
|
28
35
|
- lib/matchi/change/by.rb
|
29
36
|
- lib/matchi/change/by_at_least.rb
|
@@ -59,5 +66,5 @@ requirements: []
|
|
59
66
|
rubygems_version: 3.3.27
|
60
67
|
signing_key:
|
61
68
|
specification_version: 4
|
62
|
-
summary: "
|
69
|
+
summary: "Framework-agnostic matchers for secure, elegant Ruby testing \U0001F939"
|
63
70
|
test_files: []
|