r_spec-clone 1.2.3 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c76f25e186a2f835a2a006e3b3add6c7b94d4b070bd3a81b866747edff8291f
4
- data.tar.gz: e9203508514fde39e690e145172dd417d2dcd20eb6badc44a1cb34fc3dc7335a
3
+ metadata.gz: ef1c79c6783a2aace9f035ef5935bb556828731501ce7b92f86dcedf58e18d90
4
+ data.tar.gz: 615131453af20ba74f436f0569db9f429af2bd18589de5ee132ef69af8cd09d8
5
5
  SHA512:
6
- metadata.gz: 30681f5232bef7c695e05b169ce5fcf3882277bf981174898d586a01c756b0eec3ca88c9eecf6c02c3deae7d515e84f3bbd404202cb1aef59f63b70fa901f6ca
7
- data.tar.gz: 0e4e21aa8f6aa8d6f281ce99add7018ea6d163fab13fd8cd04863104884244a74566b11d665d3e7a7e5c0e683e474b479d18383b0cf8e7f192b7986cd7947d26
6
+ metadata.gz: bbaa63b25dc7a3d98810d686d5fb4a5cacec5a74ac04d1aab01f86dfc886ef57cf9b9ae20201aaf3a43e95bb626ce363bfe62190142904ceef8526d1d34ef9ab
7
+ data.tar.gz: e6b4d19ef401aa923f31739ab5ee5289929309d762113f82d361f52859c17f24739e18f8236e93db6d47f428e1f09a6aa4c6beabfb5ea938f0fa1f6769cf4835
data/README.md CHANGED
@@ -117,6 +117,8 @@ end
117
117
  # Success: expected to eq "foo".
118
118
  ```
119
119
 
120
+ Note: if you are wondering what the Ruby code generated by using the DSL might look like, an article presents the correspondence between each method via simple examples, available in [English](https://dev.to/cyri_/what-ruby-code-to-expect-from-a-testing-dsl-4oe1), [Chinese](https://ruby-china.org/topics/41441) and [Japanese](https://qiita.com/cyril/items/17ee758e162bae144a07).
121
+
120
122
  ### Expectations
121
123
 
122
124
  Expectations define if the value being tested (_actual_) matches a certain value or specific criteria.
@@ -135,6 +137,12 @@ expect(actual).to equal(expected) # passes if expected.equal?(actual)
135
137
  expect(actual).to be(expected) # passes if expected.equal?(actual)
136
138
  ```
137
139
 
140
+ #### Comparisons
141
+
142
+ ```ruby
143
+ expect(actual).to be_within(delta).of(expected) # passes if (expected - actual).abs <= delta
144
+ ```
145
+
138
146
  #### Regular expressions
139
147
 
140
148
  ```ruby
@@ -147,13 +155,13 @@ expect(actual).to match(expected) # passes if expected.match?(actual)
147
155
  expect { actual }.to raise_exception(expected) # passes if expected exception is raised
148
156
  ```
149
157
 
150
- #### Truth
158
+ #### True
151
159
 
152
160
  ```ruby
153
161
  expect(actual).to be_true # passes if true.equal?(actual)
154
162
  ```
155
163
 
156
- #### Untruth
164
+ #### False
157
165
 
158
166
  ```ruby
159
167
  expect(actual).to be_false # passes if false.equal?(actual)
@@ -172,6 +180,36 @@ expect(actual).to be_instance_of(expected) # passes if expected.equal?(actual
172
180
  expect(actual).to be_an_instance_of(expected) # passes if expected.equal?(actual.class)
173
181
  ```
174
182
 
183
+ #### Predicate
184
+
185
+ ```ruby
186
+ expect(actual).to be_xxx # passes if actual.xxx?
187
+ expect(actual).to be_have_xxx(:yyy) # passes if actual.has_xxx?(:yyy)
188
+ ```
189
+
190
+ ##### Examples
191
+
192
+ ```ruby
193
+ expect([]).to be_empty
194
+ expect(foo: 1).to have_key(:foo)
195
+ ```
196
+
197
+ #### Change
198
+
199
+ ```ruby
200
+ expect { object.action }.to change(object, :value).to(new)
201
+ expect { object.action }.to change(object, :value).from(old).to(new)
202
+ expect { object.action }.to change(object, :value).by(delta)
203
+ expect { object.action }.to change(object, :value).by_at_least(minimum_delta)
204
+ expect { object.action }.to change(object, :value).by_at_most(maximum_delta)
205
+ ```
206
+
207
+ #### Satisfy
208
+
209
+ ```ruby
210
+ expect(actual).to(satisfy { |value| value == expected })
211
+ ```
212
+
175
213
  ### Running specs
176
214
 
177
215
  By convention, specs live in the `spec/` directory of a project. Spec files should end with `_spec.rb` to be recognizable as such.
@@ -232,17 +270,23 @@ bundle exec rake
232
270
 
233
271
  ## Performance
234
272
 
273
+ The benchmarks compare the performance of [`r_spec-clone`](https://rubygems.org/gems/r_spec-clone) with the following frameworks (in alphabetical order):
274
+
275
+ * [`fix`](https://rubygems.org/gems/fix)
276
+ * [`minitest`](https://rubygems.org/gems/minitest)
277
+ * [`rspec`](https://rubygems.org/gems/rspec)
278
+
235
279
  ### Boot time
236
280
 
237
281
  Benchmark against [100 executions of a file containing 1 expectation](https://github.com/cyril/r_spec-clone.rb/blob/main/benchmark/boot_time/) (lower is better).
238
282
 
239
- ![Boot time](https://r-spec.dev/benchmark-boot-time.svg)
283
+ ![Boot time benchmark](https://r-spec.dev/benchmark-boot-time.svg)
240
284
 
241
- ### Run time
285
+ ### Runtime
242
286
 
243
- Benchmark against [1 execution of a file containing 1,000,000 expectations](https://github.com/cyril/r_spec-clone.rb/blob/main/benchmark/run_time/) (lower is better).
287
+ Benchmark against [1 execution of a file containing 100,000 expectations](https://github.com/cyril/r_spec-clone.rb/blob/main/benchmark/run_time/) (lower is better).
244
288
 
245
- ![Run time](https://r-spec.dev/benchmark-run-time.svg)
289
+ ![Runtime benchmark](https://r-spec.dev/benchmark-run-time.svg)
246
290
 
247
291
  ## Test suite
248
292
 
data/lib/r_spec.rb CHANGED
@@ -37,8 +37,8 @@ require_relative File.join("r_spec", "clone", "dsl")
37
37
  #
38
38
  # # Output to the console
39
39
  # # Success: expected to eq 3.
40
- # # Success: expected true to be true.
41
- # # Success: expected false to be false.
40
+ # # Success: expected to be true.
41
+ # # Success: expected to be false.
42
42
  #
43
43
  # @example An inherited definition of let
44
44
  # require "r_spec"
@@ -12,13 +12,13 @@ module RSpec
12
12
  # @return [nil] Write a pending expectation to STDOUT.
13
13
  def self.result(message)
14
14
  ::Expresenter.call(true).with(
15
- actual: new(message),
16
- error: nil,
17
- expected: self,
18
- got: false,
19
- matcher: :raise_exception,
20
- negate: true,
21
- level: :SHOULD
15
+ actual: new(message),
16
+ definition: "raise exception #{self}",
17
+ error: nil,
18
+ expected: self,
19
+ got: false,
20
+ negate: true,
21
+ level: :SHOULD
22
22
  )
23
23
  end
24
24
  end
@@ -19,7 +19,7 @@ module RSpec
19
19
  #
20
20
  # @example
21
21
  # expect("foo") # => #<RSpec::Clone::ExpectationTarget::Value:0x00007f @actual="foo">
22
- # expect { Boom } # => #<RSpec::Clone::ExpectationTarget::Block:0x00007f @callable=#<Proc:0x00007f>>
22
+ # expect { RSpec::Clone::Boom! } # => #<RSpec::Clone::ExpectationTarget::Block:0x... @callable=#<Proc:0x...>>
23
23
  #
24
24
  # @api public
25
25
  def expect(value = self.class.superclass, &block)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "matchi/rspec"
4
- require "matchi/helper"
3
+ require "matchi"
5
4
 
6
5
  module RSpec
7
6
  module Clone
@@ -11,72 +10,240 @@ module RSpec
11
10
  # This module defines a number of methods to create expectations, which
12
11
  # are automatically included into examples.
13
12
  #
14
- # It also includes a collection of expectation matchers 🤹
15
- #
16
- # @example Equivalence matcher
17
- # matcher = eql("foo") # => Matchi::Matcher::Eql.new("foo")
18
- # matcher.matches? { "foo" } # => true
19
- # matcher.matches? { "bar" } # => false
20
- #
21
- # matcher = eq("foo") # => Matchi::Matcher::Eq.new("foo")
22
- # matcher.matches? { "foo" } # => true
23
- # matcher.matches? { "bar" } # => false
24
- #
25
- # @example Identity matcher
26
- # object = "foo"
27
- #
28
- # matcher = equal(object) # => Matchi::Matcher::Equal.new(object)
29
- # matcher.matches? { object } # => true
30
- # matcher.matches? { "foo" } # => false
31
- #
32
- # matcher = be(object) # => Matchi::Matcher::Be.new(object)
33
- # matcher.matches? { object } # => true
34
- # matcher.matches? { "foo" } # => false
35
- #
36
- # @example Regular expressions matcher
37
- # matcher = match(/^foo$/) # => Matchi::Matcher::Match.new(/^foo$/)
38
- # matcher.matches? { "foo" } # => true
39
- # matcher.matches? { "bar" } # => false
40
- #
41
- # @example Expecting errors matcher
42
- # matcher = raise_exception(NameError) # => Matchi::Matcher::RaiseException.new(NameError)
43
- # matcher.matches? { Boom } # => true
44
- # matcher.matches? { true } # => false
45
- #
46
- # @example Truth matcher
47
- # matcher = be_true # => Matchi::Matcher::BeTrue.new
48
- # matcher.matches? { true } # => true
49
- # matcher.matches? { false } # => false
50
- # matcher.matches? { nil } # => false
51
- # matcher.matches? { 4 } # => false
52
- #
53
- # @example Untruth matcher
54
- # matcher = be_false # => Matchi::Matcher::BeFalse.new
55
- # matcher.matches? { false } # => true
56
- # matcher.matches? { true } # => false
57
- # matcher.matches? { nil } # => false
58
- # matcher.matches? { 4 } # => false
59
- #
60
- # @example Nil matcher
61
- # matcher = be_nil # => Matchi::Matcher::BeNil.new
62
- # matcher.matches? { nil } # => true
63
- # matcher.matches? { false } # => false
64
- # matcher.matches? { true } # => false
65
- # matcher.matches? { 4 } # => false
66
- #
67
- # @example Type/class matcher
68
- # matcher = be_instance_of(String) # => Matchi::Matcher::BeInstanceOf.new(String)
69
- # matcher.matches? { "foo" } # => true
70
- # matcher.matches? { 4 } # => false
71
- #
72
- # matcher = be_an_instance_of(String) # => Matchi::Matcher::BeAnInstanceOf.new(String)
73
- # matcher.matches? { "foo" } # => true
74
- # matcher.matches? { 4 } # => false
13
+ # It also includes a collection of expectation matchers.
75
14
  #
76
15
  # @see https://github.com/fixrb/matchi
77
- # @see https://github.com/fixrb/matchi-rspec
78
16
  module Shared
79
- include ::Matchi::Helper
17
+ # Equivalence matcher
18
+ #
19
+ # @example
20
+ # matcher = eq("foo")
21
+ # matcher.matches? { "foo" } # => true
22
+ # matcher.matches? { "bar" } # => false
23
+ #
24
+ # @param expected [#eql?] An expected equivalent object.
25
+ #
26
+ # @return [#matches?] An equivalence matcher.
27
+ #
28
+ # @api public
29
+ def eq(expected)
30
+ ::Matchi::Eq.new(expected)
31
+ end
32
+
33
+ alias eql eq
34
+
35
+ # Identity matcher
36
+ #
37
+ # @example
38
+ # object = "foo"
39
+ # matcher = be(object)
40
+ # matcher.matches? { object } # => true
41
+ # matcher.matches? { "foo" } # => false
42
+ #
43
+ # @param expected [#equal?] The expected identical object.
44
+ #
45
+ # @return [#matches?] An identity matcher.
46
+ #
47
+ # @api public
48
+ def be(expected)
49
+ ::Matchi::Be.new(expected)
50
+ end
51
+
52
+ alias equal be
53
+
54
+ # Comparisons matcher
55
+ #
56
+ # @example
57
+ # matcher = be_within(1).of(41)
58
+ # matcher.matches? { 42 } # => true
59
+ # matcher.matches? { 43 } # => false
60
+ #
61
+ # @param delta [Numeric] A numeric value.
62
+ #
63
+ # @return [#matches?] A comparison matcher.
64
+ #
65
+ # @api public
66
+ def be_within(delta)
67
+ ::Matchi::BeWithin.new(delta)
68
+ end
69
+
70
+ # Regular expressions matcher
71
+ #
72
+ # @example
73
+ # matcher = match(/^foo$/)
74
+ # matcher.matches? { "foo" } # => true
75
+ # matcher.matches? { "bar" } # => false
76
+ #
77
+ # @param expected [#match] A regular expression.
78
+ #
79
+ # @return [#matches?] A regular expression matcher.
80
+ #
81
+ # @api public
82
+ def match(expected)
83
+ ::Matchi::Match.new(expected)
84
+ end
85
+
86
+ # Expecting errors matcher
87
+ #
88
+ # @example
89
+ # matcher = raise_exception(NameError)
90
+ # matcher.matches? { RSpec::Clone::Boom! } # => true
91
+ # matcher.matches? { true } # => false
92
+ #
93
+ # @param expected [Exception, #to_s] The expected exception name.
94
+ #
95
+ # @return [#matches?] An error matcher.
96
+ #
97
+ # @api public
98
+ def raise_exception(expected)
99
+ ::Matchi::RaiseException.new(expected)
100
+ end
101
+
102
+ # True matcher
103
+ #
104
+ # @example
105
+ # matcher = be_true
106
+ # matcher.matches? { true } # => true
107
+ # matcher.matches? { false } # => false
108
+ # matcher.matches? { nil } # => false
109
+ # matcher.matches? { 4 } # => false
110
+ #
111
+ # @return [#matches?] A `true` matcher.
112
+ #
113
+ # @api public
114
+ def be_true
115
+ be(true)
116
+ end
117
+
118
+ # False matcher
119
+ #
120
+ # @example
121
+ # matcher = be_false
122
+ # matcher.matches? { false } # => true
123
+ # matcher.matches? { true } # => false
124
+ # matcher.matches? { nil } # => false
125
+ # matcher.matches? { 4 } # => false
126
+ #
127
+ # @return [#matches?] A `false` matcher.
128
+ #
129
+ # @api public
130
+ def be_false
131
+ be(false)
132
+ end
133
+
134
+ # Nil matcher
135
+ #
136
+ # @example
137
+ # matcher = be_nil
138
+ # matcher.matches? { nil } # => true
139
+ # matcher.matches? { false } # => false
140
+ # matcher.matches? { true } # => false
141
+ # matcher.matches? { 4 } # => false
142
+ #
143
+ # @return [#matches?] A `nil` matcher.
144
+ #
145
+ # @api public
146
+ def be_nil
147
+ be(nil)
148
+ end
149
+
150
+ # Type/class matcher
151
+ #
152
+ # @example
153
+ # matcher = be_an_instance_of(String)
154
+ # matcher.matches? { "foo" } # => true
155
+ # matcher.matches? { 4 } # => false
156
+ #
157
+ # @param expected [Class, #to_s] The expected class name.
158
+ #
159
+ # @return [#matches?] A type/class matcher.
160
+ #
161
+ # @api public
162
+ def be_an_instance_of(expected)
163
+ ::Matchi::BeAnInstanceOf.new(expected)
164
+ end
165
+
166
+ # Change matcher
167
+ #
168
+ # @example
169
+ # object = []
170
+ # matcher = change(object, :length).by(1)
171
+ # matcher.matches? { object << 1 } # => true
172
+ #
173
+ # object = []
174
+ # matcher = change(object, :length).by_at_least(1)
175
+ # matcher.matches? { object << 1 } # => true
176
+ #
177
+ # object = []
178
+ # matcher = change(object, :length).by_at_most(1)
179
+ # matcher.matches? { object << 1 } # => true
180
+ #
181
+ # object = "foo"
182
+ # matcher = change(object, :to_s).from("foo").to("FOO")
183
+ # matcher.matches? { object.upcase! } # => true
184
+ #
185
+ # object = "foo"
186
+ # matcher = change(object, :to_s).to("FOO")
187
+ # matcher.matches? { object.upcase! } # => true
188
+ #
189
+ # @param object [#object_id] An object.
190
+ # @param method [Symbol] The name of a method.
191
+ # @param args [Array] A list of arguments.
192
+ # @param kwargs [Hash] A list of keyword arguments.
193
+ #
194
+ # @return [#matches?] A change matcher.
195
+ #
196
+ # @api public
197
+ def change(object, method, *args, **kwargs, &block)
198
+ ::Matchi::Change.new(object, method, *args, **kwargs, &block)
199
+ end
200
+
201
+ # Satisfy matcher
202
+ #
203
+ # @example
204
+ # matcher = satisfy { |value| value == 42 }
205
+ # matcher.matches? { 42 } # => true
206
+ #
207
+ # @param expected [Proc] A block of code.
208
+ #
209
+ # @return [#matches?] A satisfy matcher.
210
+ #
211
+ # @api public
212
+ def satisfy(&expected)
213
+ ::Matchi::Satisfy.new(&expected)
214
+ end
215
+
216
+ private
217
+
218
+ # Predicate matcher, or default method missing behavior.
219
+ #
220
+ # @example Empty predicate matcher
221
+ # matcher = be_empty
222
+ # matcher.matches? { [] } # => true
223
+ # matcher.matches? { [4] } # => false
224
+ def method_missing(name, *args, **kwargs, &block)
225
+ return super unless predicate_matcher_name?(name)
226
+
227
+ ::Matchi::Predicate.new(name, *args, **kwargs, &block)
228
+ end
229
+
230
+ # :nocov:
231
+
232
+ # Hook method to return whether the obj can respond to id method or not.
233
+ def respond_to_missing?(name, include_private = false)
234
+ predicate_matcher_name?(name) || super
235
+ end
236
+
237
+ # :nocov:
238
+
239
+ # Predicate matcher name detector.
240
+ #
241
+ # @param name [Array, Symbol] The name of a potential predicate matcher.
242
+ #
243
+ # @return [Boolean] Indicates if it is a predicate matcher name or not.
244
+ def predicate_matcher_name?(name)
245
+ name.start_with?("be_", "have_") && !name.end_with?("!", "?")
246
+ end
80
247
  end
81
248
  end
82
249
  end
@@ -95,13 +95,13 @@ module RSpec
95
95
  # `Kernel.exit(false)` with a failure message written to STDERR.
96
96
  def result(passed, actual:, error:, got:, matcher:, negate:)
97
97
  Console.passed_spec ::Expresenter.call(passed).with(
98
- actual: actual,
99
- error: error,
100
- expected: matcher.expected,
101
- got: got,
102
- negate: negate,
103
- matcher: matcher.class.to_sym,
104
- level: :MUST
98
+ actual: actual,
99
+ definition: matcher.to_s,
100
+ error: error,
101
+ expected: matcher.expected,
102
+ got: got,
103
+ negate: negate,
104
+ level: :MUST
105
105
  )
106
106
  rescue ::Expresenter::Fail => e
107
107
  Console.failed_spec(e)
@@ -29,7 +29,7 @@ module RSpec
29
29
  # @raise (see Base#absolute_requirement)
30
30
  def absolute_requirement(matcher:, negate:)
31
31
  super(
32
- ::TestTube.invoke(isolation: false, matcher: matcher, negate: negate, &@input),
32
+ ::TestTube.invoke(isolate: false, matcher: matcher, negate: negate, &@input),
33
33
  matcher: matcher,
34
34
  negate: negate
35
35
  )
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: r_spec-clone
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.5.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: 2021-07-15 00:00:00.000000000 Z
11
+ date: 2021-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: expresenter
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.3.0
19
+ version: 1.4.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.3.0
26
+ version: 1.4.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: matchi-rspec
28
+ name: matchi
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.2.0
33
+ version: 3.3.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.2.0
40
+ version: 3.3.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: test_tube
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 2.0.0
47
+ version: 2.1.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 2.0.0
54
+ version: 2.1.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement