matchi 4.1.1 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|