r_spec-clone 1.4.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.md +2 -2
- data/README.md +33 -34
- data/lib/r_spec/clone/dsl.rb +17 -216
- data/lib/r_spec/clone/expectation_helper/it.rb +1 -1
- data/lib/r_spec/clone/expectation_helper/shared.rb +33 -1
- data/lib/r_spec/clone/expectation_target/base.rb +7 -3
- data/lib/r_spec/clone/{console.rb → logger.rb} +8 -4
- data/lib/r_spec.rb +0 -87
- metadata +10 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6014d4cd0ffe271d71ff599cdba7ce291b5329c062aeea611c292dcb9d24d822
|
4
|
+
data.tar.gz: 96e14ce3c34c9d5d6e9a6c49769c94db2562e2a14eaeae94e6415884f2f5c7eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7bd806d2a1a220c86320a6a8fa93bf9b19f60ac2a0b4f03f510f727d6d0132995c0dde783659923c2031d7f0864b63f5137976c136d3c1c90a62ecd8996ef25
|
7
|
+
data.tar.gz: 4eda5fc59e05cb4a2a20e18cc403a89985552737b551d2f343fedf3b9f98f6dd572ef977c0164926b5b0703a9b91b2901189bcad6823618558c908df6a869c4a
|
data/LICENSE.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
The MIT License
|
1
|
+
# The MIT License
|
2
2
|
|
3
|
-
Copyright (c) 2015-
|
3
|
+
Copyright (c) 2015-2022 Cyril Kato
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -15,14 +15,14 @@ A minimalist __RSpec clone__ with all the essentials.
|
|
15
15
|
|
16
16
|
## Project goals
|
17
17
|
|
18
|
-
1. Keep
|
19
|
-
2.
|
18
|
+
1. Keep code complexity low, avoid false negatives and false positives.
|
19
|
+
2. Load specifications in simple, atomic and thread safe Ruby primitives.
|
20
20
|
3. Avoid overloading the interface with additional alternative syntaxes.
|
21
|
-
4. Provide
|
21
|
+
4. Provide the basics of DSL RSpec to write tests.
|
22
22
|
|
23
23
|
## Some differences
|
24
24
|
|
25
|
-
* There is no option to
|
25
|
+
* There is no option to enable monkey-patching.
|
26
26
|
* It does not rely on [hacks such as `at_exit` hook](https://blog.arkency.com/2013/06/are-we-abusing-at-exit/) to trigger the tests.
|
27
27
|
* Malicious _actual values_ cannot [hack results](https://asciinema.org/a/423547?autoplay=1&speed=2).
|
28
28
|
* If no `subject` has been explicitly determined, none is defined.
|
@@ -31,8 +31,9 @@ A minimalist __RSpec clone__ with all the essentials.
|
|
31
31
|
* [Arbitrary helper methods](https://relishapp.com/rspec/rspec-core/v/3-10/docs/helper-methods/arbitrary-helper-methods) are not exposed to examples.
|
32
32
|
* The `let` method defines a helper method rather than a memoized helper method.
|
33
33
|
* The one-liner `is_expected` syntax also works with block expectations.
|
34
|
-
* `subject`, `before
|
35
|
-
*
|
34
|
+
* `subject`, `before` and `let` definitions must come before examples.
|
35
|
+
* The execution of the test suite stops as soon as an error is detected.
|
36
|
+
* Each `context` block isolates its tests and possible side effects.
|
36
37
|
|
37
38
|
## Installation
|
38
39
|
|
@@ -72,9 +73,10 @@ To use the `RSpec` module and its DSL, you need to add `require "r_spec"` to you
|
|
72
73
|
Many projects use a custom spec helper which organizes these includes.
|
73
74
|
|
74
75
|
Concrete test cases are defined in `it` blocks.
|
75
|
-
An optional descriptive string
|
76
|
+
An optional (but recommended) descriptive string or module indicates the purpose of the test and a block contains the main logic of the test.
|
76
77
|
|
77
|
-
Test cases that have been defined or outlined but are not yet expected to work can be defined using `pending` instead of `it`.
|
78
|
+
Test cases that have been defined or outlined but are not yet expected to work can be defined using `pending` instead of `it`.
|
79
|
+
They will not be run but show up in the spec report as pending.
|
78
80
|
|
79
81
|
An `it` block contains an example that should invoke the code to be tested and define what is expected of it.
|
80
82
|
Each example can contain multiple expectations, but it should test only one specific behaviour.
|
@@ -85,7 +87,7 @@ To express an expectation, wrap an object or block in `expect`, call `to` (or `n
|
|
85
87
|
If the expectation is met, code execution continues.
|
86
88
|
Otherwise the example has _failed_ and other code will not be executed.
|
87
89
|
|
88
|
-
In test files, specs
|
90
|
+
In test files, specs can be structured by example groups which are defined by `describe` and `context` sections.
|
89
91
|
Typically a top level `describe` defines the outer unit (such as a class) to be tested by the spec.
|
90
92
|
Further `describe` sections can be nested within the outer unit to specify smaller units under test (such as individual methods).
|
91
93
|
|
@@ -96,28 +98,10 @@ For unit tests, it is recommended to follow the conventions for method names:
|
|
96
98
|
|
97
99
|
To establish certain contexts — think _empty array_ versus _array with elements_ — the `context` method may be used to communicate this to the reader.
|
98
100
|
|
99
|
-
|
101
|
+
Unlike a `describe` block, all specifications executed within a `context` are isolated in a subprocess.
|
102
|
+
This prevents possible side effects on the Ruby object environment from being propagated outside their context, which could alter the result of the unit test suite.
|
100
103
|
|
101
|
-
|
102
|
-
app = "foo"
|
103
|
-
|
104
|
-
RSpec.describe "Side effects per example" do
|
105
|
-
it! "runs the example in isolation" do
|
106
|
-
expect { app.gsub!("foo", "bar") }.to eq "bar"
|
107
|
-
expect(app).to eq "bar"
|
108
|
-
end
|
109
|
-
|
110
|
-
it "runs the example" do
|
111
|
-
expect(app).to eq "foo"
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# Success: expected to eq "bar".
|
116
|
-
# Success: expected to eq "bar".
|
117
|
-
# Success: expected to eq "foo".
|
118
|
-
```
|
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).
|
104
|
+
Note: if you are wondering what kind of code might be generated by the DSL, an article that shows the dynamic transcription of the main methods with simple examples is available in [Chinese](https://ruby-china.org/topics/41441), in [English](https://dev.to/cyri_/what-ruby-code-to-expect-from-a-testing-dsl-4oe1) and in [Japanese](https://qiita.com/cyril/items/17ee758e162bae144a07).
|
121
105
|
|
122
106
|
### Expectations
|
123
107
|
|
@@ -180,9 +164,24 @@ expect(actual).to be_instance_of(expected) # passes if expected.equal?(actual
|
|
180
164
|
expect(actual).to be_an_instance_of(expected) # passes if expected.equal?(actual.class)
|
181
165
|
```
|
182
166
|
|
167
|
+
#### Predicate
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
expect(actual).to be_xxx # passes if actual.xxx?
|
171
|
+
expect(actual).to be_have_xxx(:yyy) # passes if actual.has_xxx?(:yyy)
|
172
|
+
```
|
173
|
+
|
174
|
+
##### Examples
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
expect([]).to be_empty
|
178
|
+
expect(foo: 1).to have_key(:foo)
|
179
|
+
```
|
180
|
+
|
183
181
|
#### Change
|
184
182
|
|
185
183
|
```ruby
|
184
|
+
expect { object.action }.to change(object, :value).to(new)
|
186
185
|
expect { object.action }.to change(object, :value).from(old).to(new)
|
187
186
|
expect { object.action }.to change(object, :value).by(delta)
|
188
187
|
expect { object.action }.to change(object, :value).by_at_least(minimum_delta)
|
@@ -269,7 +268,7 @@ Benchmark against [100 executions of a file containing 1 expectation](https://gi
|
|
269
268
|
|
270
269
|
### Runtime
|
271
270
|
|
272
|
-
Benchmark against [1 execution of a file containing
|
271
|
+
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).
|
273
272
|
|
274
273
|
![Runtime benchmark](https://r-spec.dev/benchmark-run-time.svg)
|
275
274
|
|
@@ -281,7 +280,7 @@ __RSpec clone__'s specifications are self-described here: [spec/](https://github
|
|
281
280
|
|
282
281
|
* Home page: [https://r-spec.dev/](https://r-spec.dev/)
|
283
282
|
* Cheatsheet: [https://r-spec.dev/cheatsheet.html](https://r-spec.dev/cheatsheet.html)
|
284
|
-
* Blog post: [https://
|
283
|
+
* Blog post: [https://cyrilllllll.medium.com/introducing-a-new-rspec-850d48c0f901](https://cyrilllllll.medium.com/introducing-a-new-rspec-850d48c0f901)
|
285
284
|
* Source code: [https://github.com/cyril/r_spec-clone.rb](https://github.com/cyril/r_spec-clone.rb)
|
286
285
|
* API Doc: [https://rubydoc.info/gems/r_spec-clone](https://rubydoc.info/gems/r_spec-clone)
|
287
286
|
* Twitter: [https://twitter.com/cyri\_](https://twitter.com/cyri\_)
|
@@ -295,9 +294,9 @@ Without RSpec, this clone would not have been possible.
|
|
295
294
|
|
296
295
|
## Buy me a coffee ☕
|
297
296
|
|
298
|
-
If you like this project, please consider making a small donation
|
297
|
+
If you like this project, please consider making a small donation.
|
299
298
|
|
300
|
-
[![Donate](https://img.shields.io/badge/Donate-
|
299
|
+
[![Donate](https://img.shields.io/badge/Donate-cyr.eth-purple.svg)](https://etherscan.io/address/cyr.eth)
|
301
300
|
|
302
301
|
## Versioning
|
303
302
|
|
data/lib/r_spec/clone/dsl.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "logger"
|
4
4
|
require_relative "error"
|
5
5
|
require_relative "expectation_helper"
|
6
6
|
|
@@ -9,9 +9,8 @@ module RSpec
|
|
9
9
|
# Abstract class for handling the domain-specific language.
|
10
10
|
class Dsl
|
11
11
|
BEFORE_METHOD = :initialize
|
12
|
-
AFTER_METHOD = :terminate
|
13
12
|
|
14
|
-
private_constant :BEFORE_METHOD
|
13
|
+
private_constant :BEFORE_METHOD
|
15
14
|
|
16
15
|
# Executes the given block before each spec in the current context runs.
|
17
16
|
#
|
@@ -53,35 +52,6 @@ module RSpec
|
|
53
52
|
private BEFORE_METHOD
|
54
53
|
end
|
55
54
|
|
56
|
-
# Executes the given block after each spec in the current context runs.
|
57
|
-
#
|
58
|
-
# @example
|
59
|
-
# require "r_spec"
|
60
|
-
#
|
61
|
-
# RSpec.describe Integer do
|
62
|
-
# after do
|
63
|
-
# puts "That is the answer to everything."
|
64
|
-
# end
|
65
|
-
#
|
66
|
-
# it { expect(42).to be 42 }
|
67
|
-
# end
|
68
|
-
#
|
69
|
-
# # Output to the console
|
70
|
-
# # Success: expected to be 42.
|
71
|
-
# # That is the answer to everything.
|
72
|
-
#
|
73
|
-
# @param block [Proc] The content to execute at the class initialization.
|
74
|
-
#
|
75
|
-
# @api public
|
76
|
-
def self.after(&block)
|
77
|
-
define_method(AFTER_METHOD) do
|
78
|
-
instance_exec(&block)
|
79
|
-
super()
|
80
|
-
end
|
81
|
-
|
82
|
-
private AFTER_METHOD
|
83
|
-
end
|
84
|
-
|
85
55
|
# Sets a user-defined property.
|
86
56
|
#
|
87
57
|
# @example
|
@@ -110,7 +80,7 @@ module RSpec
|
|
110
80
|
#
|
111
81
|
# @api public
|
112
82
|
def self.let(name, *args, **kwargs, &block)
|
113
|
-
raise Error::ReservedMethod if
|
83
|
+
raise Error::ReservedMethod if BEFORE_METHOD.equal?(name.to_sym)
|
114
84
|
|
115
85
|
private define_method(name, *args, **kwargs, &block)
|
116
86
|
end
|
@@ -173,50 +143,14 @@ module RSpec
|
|
173
143
|
|
174
144
|
# :nocov:
|
175
145
|
|
176
|
-
# Runs a describe example group in a subprocess to isolate side effects.
|
177
|
-
#
|
178
|
-
# @example
|
179
|
-
# $app = "foo"
|
180
|
-
#
|
181
|
-
# require "r_spec"
|
182
|
-
#
|
183
|
-
# RSpec.describe "Scoped side effects" do
|
184
|
-
# describe! "#gsub!" do
|
185
|
-
# before do
|
186
|
-
# $app.gsub!("o", "0")
|
187
|
-
# end
|
188
|
-
#
|
189
|
-
# context! "when isolated in the context" do
|
190
|
-
# before do
|
191
|
-
# $app.gsub!("f", "F")
|
192
|
-
# end
|
193
|
-
#
|
194
|
-
# it { expect($app).to eq "F00" }
|
195
|
-
# end
|
196
|
-
#
|
197
|
-
# it { expect($app).to eq "f00" }
|
198
|
-
# end
|
199
|
-
#
|
200
|
-
# it { expect($app).to eq "foo" }
|
201
|
-
# end
|
202
|
-
#
|
203
|
-
# # Output to the console
|
204
|
-
# # Success: expected to eq "F00".
|
205
|
-
# # Success: expected to eq "f00".
|
206
|
-
# # Success: expected to eq "foo".
|
207
|
-
#
|
208
|
-
# @param (see #describe)
|
209
|
-
#
|
210
|
-
# @api public
|
211
|
-
def self.describe!(const, &block)
|
212
|
-
fork! { describe(const, &block) }
|
213
|
-
end
|
214
|
-
|
215
|
-
# :nocov:
|
216
|
-
|
217
146
|
# Defines an example group that establishes a specific context, like
|
218
147
|
# _empty array_ versus _array with elements_.
|
219
148
|
#
|
149
|
+
# Unlike a `describe` block, all specifications executed within a
|
150
|
+
# `context` are isolated in a subprocess. This prevents possible side
|
151
|
+
# effects on the Ruby object environment from being propagated outside
|
152
|
+
# their context, which could alter the result of the unit test suite.
|
153
|
+
#
|
220
154
|
# @example
|
221
155
|
# require "r_spec"
|
222
156
|
#
|
@@ -240,50 +174,8 @@ module RSpec
|
|
240
174
|
#
|
241
175
|
# @api public
|
242
176
|
def self.context(_description, &block)
|
243
|
-
|
244
|
-
|
245
|
-
end
|
246
|
-
|
247
|
-
# :nocov:
|
248
|
-
|
249
|
-
# Runs a context example group in a subprocess to isolate side effects.
|
250
|
-
#
|
251
|
-
# @example
|
252
|
-
# app = "Hello, world!"
|
253
|
-
#
|
254
|
-
# require "r_spec"
|
255
|
-
#
|
256
|
-
# RSpec.describe String do
|
257
|
-
# subject do
|
258
|
-
# app
|
259
|
-
# end
|
260
|
-
#
|
261
|
-
# before do
|
262
|
-
# subject.gsub!("world", person)
|
263
|
-
# end
|
264
|
-
#
|
265
|
-
# context! "when Alice is greeted" do
|
266
|
-
# let(:person) { "Alice" }
|
267
|
-
#
|
268
|
-
# it { is_expected.to eq "Hello, Alice!" }
|
269
|
-
# end
|
270
|
-
#
|
271
|
-
# context! "when Bob is greeted" do
|
272
|
-
# let(:person) { "Bob" }
|
273
|
-
#
|
274
|
-
# it { is_expected.to eq "Hello, Bob!" }
|
275
|
-
# end
|
276
|
-
# end
|
277
|
-
#
|
278
|
-
# # Output to the console
|
279
|
-
# # Success: expected to eq "Hello, Alice!".
|
280
|
-
# # Success: expected to eq "Hello, Bob!".
|
281
|
-
#
|
282
|
-
# @param (see #context)
|
283
|
-
#
|
284
|
-
# @api public
|
285
|
-
def self.context!(description, &block)
|
286
|
-
fork! { context(description, &block) }
|
177
|
+
pid = ::Process.fork { ::Class.new(self).instance_eval(&block) }
|
178
|
+
exit false unless ::Process::Status.wait(pid).exitstatus.zero?
|
287
179
|
end
|
288
180
|
|
289
181
|
# :nocov:
|
@@ -329,42 +221,8 @@ module RSpec
|
|
329
221
|
#
|
330
222
|
# @api public
|
331
223
|
def self.it(_name = nil, &block)
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
# :nocov:
|
336
|
-
|
337
|
-
# Runs a concrete test case in a subprocess to isolate side effects.
|
338
|
-
#
|
339
|
-
# @example
|
340
|
-
# app = "foo"
|
341
|
-
#
|
342
|
-
# require "r_spec"
|
343
|
-
#
|
344
|
-
# RSpec.describe "Side effects per example" do
|
345
|
-
# it! "runs the example in isolation" do
|
346
|
-
# expect { app.gsub!("foo", "bar") }.to eq "bar"
|
347
|
-
# expect(app).to eq "bar"
|
348
|
-
# end
|
349
|
-
#
|
350
|
-
# it "runs the example" do
|
351
|
-
# expect(app).to eq "foo"
|
352
|
-
# end
|
353
|
-
# end
|
354
|
-
#
|
355
|
-
# # Output to the console
|
356
|
-
# # Success: expected to eq "bar".
|
357
|
-
# # Success: expected to eq "bar".
|
358
|
-
# # Success: expected to eq "foo".
|
359
|
-
#
|
360
|
-
# @param (see #it)
|
361
|
-
#
|
362
|
-
# @raise (see ExpectationTarget::Base#result)
|
363
|
-
# @return (see ExpectationTarget::Base#result)
|
364
|
-
#
|
365
|
-
# @api public
|
366
|
-
def self.it!(name = nil, &block)
|
367
|
-
fork! { it(name, &block) }
|
224
|
+
Logger.source(*block.source_location)
|
225
|
+
example_without_attribute.new.instance_eval(&block)
|
368
226
|
end
|
369
227
|
|
370
228
|
# :nocov:
|
@@ -416,43 +274,9 @@ module RSpec
|
|
416
274
|
#
|
417
275
|
# @api public
|
418
276
|
def self.its(attribute, *args, **kwargs, &block)
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
# :nocov:
|
423
|
-
|
424
|
-
# Runs a single concrete test case in a subprocess to isolate side
|
425
|
-
# effects.
|
426
|
-
#
|
427
|
-
# @example
|
428
|
-
# app = "foo"
|
429
|
-
#
|
430
|
-
# require "r_spec"
|
431
|
-
#
|
432
|
-
# RSpec.describe "Isolated side effect" do
|
433
|
-
# subject do
|
434
|
-
# app
|
435
|
-
# end
|
436
|
-
#
|
437
|
-
# its!(:upcase) { is_expected.to eq "FOO" }
|
438
|
-
#
|
439
|
-
# it "tests the original value" do
|
440
|
-
# expect(app).to eq "foo"
|
441
|
-
# end
|
442
|
-
# end
|
443
|
-
#
|
444
|
-
# # Output to the console
|
445
|
-
# # Success: expected to eq "FOO".
|
446
|
-
# # Success: expected to eq "foo".
|
447
|
-
#
|
448
|
-
# @param (see #it)
|
449
|
-
#
|
450
|
-
# @raise (see ExpectationTarget::Base#result)
|
451
|
-
# @return (see ExpectationTarget::Base#result)
|
452
|
-
#
|
453
|
-
# @api public
|
454
|
-
def self.its!(attribute, *args, **kwargs, &block)
|
455
|
-
fork! { its(attribute, *args, **kwargs, &block) }
|
277
|
+
Logger.source(*block.source_location)
|
278
|
+
example_with_attribute(attribute, *args, **kwargs).new
|
279
|
+
.instance_eval(&block)
|
456
280
|
end
|
457
281
|
|
458
282
|
# :nocov:
|
@@ -483,7 +307,7 @@ module RSpec
|
|
483
307
|
#
|
484
308
|
# @api public
|
485
309
|
def self.pending(message)
|
486
|
-
|
310
|
+
Logger.passed_spec Error::PendingExpectation.result(message)
|
487
311
|
end
|
488
312
|
|
489
313
|
# Example class for concrete test case.
|
@@ -505,26 +329,7 @@ module RSpec
|
|
505
329
|
end
|
506
330
|
end
|
507
331
|
|
508
|
-
|
509
|
-
def self.fork!(&block)
|
510
|
-
pid = fork(&block)
|
511
|
-
thread = ::Process.detach(pid)
|
512
|
-
exitstatus = thread.join.value.exitstatus
|
513
|
-
exit false unless exitstatus.zero?
|
514
|
-
end
|
515
|
-
|
516
|
-
# Execution of specifications.
|
517
|
-
def self.run(example, &block)
|
518
|
-
example.instance_eval(&block)
|
519
|
-
rescue ::SystemExit
|
520
|
-
Console.source(*block.source_location)
|
521
|
-
|
522
|
-
exit false
|
523
|
-
ensure
|
524
|
-
example&.send(AFTER_METHOD)
|
525
|
-
end
|
526
|
-
|
527
|
-
private_class_method :example_without_attribute, :example_with_attribute, :fork!, :run
|
332
|
+
private_class_method :example_without_attribute, :example_with_attribute
|
528
333
|
|
529
334
|
private
|
530
335
|
|
@@ -539,10 +344,6 @@ module RSpec
|
|
539
344
|
def subject
|
540
345
|
raise Error::UndefinedSubject, "subject not explicitly defined"
|
541
346
|
end
|
542
|
-
|
543
|
-
define_method(AFTER_METHOD) do
|
544
|
-
# do nothing by default
|
545
|
-
end
|
546
347
|
end
|
547
348
|
end
|
548
349
|
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:
|
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)
|
@@ -87,7 +87,7 @@ module RSpec
|
|
87
87
|
#
|
88
88
|
# @example
|
89
89
|
# matcher = raise_exception(NameError)
|
90
|
-
# matcher.matches? { Boom } # => true
|
90
|
+
# matcher.matches? { RSpec::Clone::Boom! } # => true
|
91
91
|
# matcher.matches? { true } # => false
|
92
92
|
#
|
93
93
|
# @param expected [Exception, #to_s] The expected exception name.
|
@@ -212,6 +212,38 @@ module RSpec
|
|
212
212
|
def satisfy(&expected)
|
213
213
|
::Matchi::Satisfy.new(&expected)
|
214
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
|
215
247
|
end
|
216
248
|
end
|
217
249
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "expresenter"
|
4
4
|
require "test_tube"
|
5
5
|
|
6
|
-
require_relative File.join("..", "
|
6
|
+
require_relative File.join("..", "logger")
|
7
7
|
|
8
8
|
module RSpec
|
9
9
|
module Clone
|
@@ -82,6 +82,8 @@ module RSpec
|
|
82
82
|
test.got.equal?(true)
|
83
83
|
end
|
84
84
|
|
85
|
+
# :nocov:
|
86
|
+
|
85
87
|
# @param passed [Boolean] The high expectation passed or failed.
|
86
88
|
# @param actual [#object_id] The actual value.
|
87
89
|
# @param error [Exception, nil] Any raised exception.
|
@@ -94,7 +96,7 @@ module RSpec
|
|
94
96
|
# @raise [SystemExit] Terminate execution immediately by calling
|
95
97
|
# `Kernel.exit(false)` with a failure message written to STDERR.
|
96
98
|
def result(passed, actual:, error:, got:, matcher:, negate:)
|
97
|
-
|
99
|
+
Logger.passed_spec ::Expresenter.call(passed).with(
|
98
100
|
actual: actual,
|
99
101
|
definition: matcher.to_s,
|
100
102
|
error: error,
|
@@ -104,8 +106,10 @@ module RSpec
|
|
104
106
|
level: :MUST
|
105
107
|
)
|
106
108
|
rescue ::Expresenter::Fail => e
|
107
|
-
|
109
|
+
Logger.failed_spec(e)
|
108
110
|
end
|
111
|
+
|
112
|
+
# :nocov:
|
109
113
|
end
|
110
114
|
end
|
111
115
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RSpec
|
4
4
|
module Clone
|
5
5
|
# Send log messages to the console.
|
6
|
-
module
|
6
|
+
module Logger
|
7
7
|
# @param report [::Expresenter::Pass] Passed expectation result presenter.
|
8
8
|
#
|
9
9
|
# @see https://github.com/fixrb/expresenter
|
@@ -13,17 +13,21 @@ module RSpec
|
|
13
13
|
puts report.colored_string
|
14
14
|
end
|
15
15
|
|
16
|
+
# :nocov:
|
17
|
+
|
16
18
|
# @param report [::Expresenter::Fail] Failed expectation result presenter.
|
17
19
|
#
|
18
20
|
# @see https://github.com/fixrb/expresenter
|
19
21
|
#
|
20
|
-
# @raise [SystemExit] Terminate execution immediately with
|
22
|
+
# @raise [SystemExit] Terminate execution immediately with message.
|
21
23
|
def self.failed_spec(report)
|
22
24
|
abort report.colored_string
|
23
25
|
end
|
24
26
|
|
25
|
-
#
|
26
|
-
|
27
|
+
# :nocov:
|
28
|
+
|
29
|
+
# The Ruby source filename and line number containing this method or nil
|
30
|
+
# if this method was not defined in Ruby (i.e. native).
|
27
31
|
#
|
28
32
|
# @param filename [String, nil] The Ruby source filename.
|
29
33
|
# @param line [Integer, nil] The Ruby source line number.
|
data/lib/r_spec.rb
CHANGED
@@ -89,36 +89,6 @@ module RSpec
|
|
89
89
|
|
90
90
|
# :nocov:
|
91
91
|
|
92
|
-
# Runs a context example group in a subprocess to isolate side effects.
|
93
|
-
#
|
94
|
-
# @example
|
95
|
-
# str = "Hello, world!"
|
96
|
-
#
|
97
|
-
# require "r_spec"
|
98
|
-
#
|
99
|
-
# RSpec.context! "when a string becomes uppercase" do
|
100
|
-
# before do
|
101
|
-
# str.upcase!
|
102
|
-
# end
|
103
|
-
#
|
104
|
-
# it { expect(str).to eq "HELLO, WORLD!" }
|
105
|
-
# end
|
106
|
-
#
|
107
|
-
# # Output to the console
|
108
|
-
# # Success: expected to eq "HELLO, WORLD!".
|
109
|
-
#
|
110
|
-
# RSpec.it { expect(str).to eq "Hello, world!" }
|
111
|
-
#
|
112
|
-
# # Output to the console
|
113
|
-
# # Success: expected to eq "Hello, world!".
|
114
|
-
#
|
115
|
-
# @param (see #context)
|
116
|
-
def self.context!(description, &block)
|
117
|
-
Clone::Dsl.context!(description, &block)
|
118
|
-
end
|
119
|
-
|
120
|
-
# :nocov:
|
121
|
-
|
122
92
|
# Defines an example group that describes a unit to be tested.
|
123
93
|
#
|
124
94
|
# @example
|
@@ -153,36 +123,6 @@ module RSpec
|
|
153
123
|
|
154
124
|
# :nocov:
|
155
125
|
|
156
|
-
# Runs a describe example group in a subprocess to isolate side effects.
|
157
|
-
#
|
158
|
-
# @example
|
159
|
-
# $app = "foo"
|
160
|
-
#
|
161
|
-
# require "r_spec"
|
162
|
-
#
|
163
|
-
# RSpec.describe! "#gsub!" do
|
164
|
-
# before do
|
165
|
-
# $app.gsub!("o", "0")
|
166
|
-
# end
|
167
|
-
#
|
168
|
-
# it { expect($app).to eq "f00" }
|
169
|
-
# end
|
170
|
-
#
|
171
|
-
# # Output to the console
|
172
|
-
# # Success: expected to eq "f00".
|
173
|
-
#
|
174
|
-
# RSpec.it { expect($app).to eq "foo" }
|
175
|
-
#
|
176
|
-
# # Output to the console
|
177
|
-
# # Success: expected to eq "foo".
|
178
|
-
#
|
179
|
-
# @param (see #describe)
|
180
|
-
def self.describe!(const, &block)
|
181
|
-
Clone::Dsl.describe!(const, &block)
|
182
|
-
end
|
183
|
-
|
184
|
-
# :nocov:
|
185
|
-
|
186
126
|
# Defines a concrete test case.
|
187
127
|
#
|
188
128
|
# The test is performed by the block supplied to &block.
|
@@ -209,33 +149,6 @@ module RSpec
|
|
209
149
|
|
210
150
|
# :nocov:
|
211
151
|
|
212
|
-
# Runs a concrete test case in a subprocess to isolate side effects.
|
213
|
-
#
|
214
|
-
# @example
|
215
|
-
# app = "Hello, world!"
|
216
|
-
#
|
217
|
-
# require "r_spec"
|
218
|
-
#
|
219
|
-
# RSpec.it! { expect(app.gsub!("world", "Alice")).to eq "Hello, Alice!" }
|
220
|
-
#
|
221
|
-
# # Output to the console
|
222
|
-
# # Success: expected to eq "Hello, Alice!".
|
223
|
-
#
|
224
|
-
# RSpec.it { expect(app).to eq "Hello, world!" }
|
225
|
-
#
|
226
|
-
# # Output to the console
|
227
|
-
# # Success: expected to eq "Hello, world!".
|
228
|
-
#
|
229
|
-
# @param (see #it)
|
230
|
-
#
|
231
|
-
# @raise (see ExpectationTarget::Base#result)
|
232
|
-
# @return (see ExpectationTarget::Base#result)
|
233
|
-
def self.it!(name = nil, &block)
|
234
|
-
Clone::Dsl.it!(name, &block)
|
235
|
-
end
|
236
|
-
|
237
|
-
# :nocov:
|
238
|
-
|
239
152
|
# Defines a pending test case.
|
240
153
|
#
|
241
154
|
# `&block` is never evaluated. It can be used to describe behaviour that is
|
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.
|
4
|
+
version: 1.7.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: 2022-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: expresenter
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 3.
|
33
|
+
version: 3.3.1
|
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: 3.
|
40
|
+
version: 3.3.1
|
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.1.
|
47
|
+
version: 2.1.3
|
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.1.
|
54
|
+
version: 2.1.3
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -188,7 +188,6 @@ files:
|
|
188
188
|
- README.md
|
189
189
|
- lib/r_spec.rb
|
190
190
|
- lib/r_spec/clone.rb
|
191
|
-
- lib/r_spec/clone/console.rb
|
192
191
|
- lib/r_spec/clone/dsl.rb
|
193
192
|
- lib/r_spec/clone/error.rb
|
194
193
|
- lib/r_spec/clone/error/pending_expectation.rb
|
@@ -203,6 +202,7 @@ files:
|
|
203
202
|
- lib/r_spec/clone/expectation_target/base.rb
|
204
203
|
- lib/r_spec/clone/expectation_target/block.rb
|
205
204
|
- lib/r_spec/clone/expectation_target/value.rb
|
205
|
+
- lib/r_spec/clone/logger.rb
|
206
206
|
homepage: https://r-spec.dev/
|
207
207
|
licenses:
|
208
208
|
- MIT
|
@@ -211,6 +211,7 @@ metadata:
|
|
211
211
|
documentation_uri: https://rubydoc.info/gems/r_spec-clone
|
212
212
|
source_code_uri: https://github.com/cyril/r_spec-clone.rb
|
213
213
|
wiki_uri: https://github.com/cyril/r_spec-clone.rb/wiki
|
214
|
+
rubygems_mfa_required: 'true'
|
214
215
|
post_install_message:
|
215
216
|
rdoc_options: []
|
216
217
|
require_paths:
|
@@ -219,14 +220,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
219
220
|
requirements:
|
220
221
|
- - ">="
|
221
222
|
- !ruby/object:Gem::Version
|
222
|
-
version:
|
223
|
+
version: 3.0.4
|
223
224
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
224
225
|
requirements:
|
225
226
|
- - ">="
|
226
227
|
- !ruby/object:Gem::Version
|
227
228
|
version: '0'
|
228
229
|
requirements: []
|
229
|
-
rubygems_version: 3.
|
230
|
+
rubygems_version: 3.2.33
|
230
231
|
signing_key:
|
231
232
|
specification_version: 4
|
232
233
|
summary: A minimalist RSpec clone
|