r_spec-clone 1.2.0 → 1.2.4
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 +28 -10
- data/lib/r_spec.rb +72 -21
- data/lib/r_spec/clone/console.rb +0 -2
- data/lib/r_spec/clone/dsl.rb +181 -29
- data/lib/r_spec/clone/error.rb +0 -2
- data/lib/r_spec/clone/error/pending_expectation.rb +0 -2
- data/lib/r_spec/clone/error/reserved_method.rb +0 -2
- data/lib/r_spec/clone/error/undefined_described_class.rb +0 -2
- data/lib/r_spec/clone/error/undefined_subject.rb +0 -2
- data/lib/r_spec/clone/expectation_helper/it.rb +3 -3
- data/lib/r_spec/clone/expectation_helper/its.rb +1 -1
- data/lib/r_spec/clone/expectation_helper/shared.rb +3 -4
- data/lib/r_spec/clone/expectation_target.rb +2 -4
- data/lib/r_spec/clone/expectation_target/base.rb +35 -7
- data/lib/r_spec/clone/expectation_target/block.rb +6 -17
- data/lib/r_spec/clone/expectation_target/value.rb +6 -16
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e5c67c5d900bbe71f8e647c1fd1c4e5bd6a96e01b9db3d228918dedb755edd2
|
4
|
+
data.tar.gz: 3907ef8dab4a2bb8c751eacf281c5f643b2f3a9c774fbb71ec1cfc0707072dea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 758790442ee21c56a0d40f98385ad7bdfbcbc88d527fc266f86641623fed9c172b68144e538a4565dd19e211b53b3412d847813978991853bfe2d8ab82a67cd7
|
7
|
+
data.tar.gz: fd4c45945f0f84de6361f68aaec5d84feb6f9983f27248b42abb5757b9c17a3fd15b00e4c0ab7314a6050d6234f8e3b8e5ef997d87df26b34e7f199322dc621d
|
data/README.md
CHANGED
@@ -6,6 +6,7 @@ A minimalist __RSpec clone__ with all the essentials.
|
|
6
6
|
|
7
7
|
## Status
|
8
8
|
|
9
|
+
[](https://r-spec.dev/)
|
9
10
|
[](https://github.com/cyril/r_spec-clone.rb/releases)
|
10
11
|
[](https://rubydoc.info/github/cyril/r_spec-clone.rb/main)
|
11
12
|
[](https://github.com/cyril/r_spec-clone.rb/actions?query=workflow%3Aci+branch%3Amain)
|
@@ -23,7 +24,7 @@ A minimalist __RSpec clone__ with all the essentials.
|
|
23
24
|
|
24
25
|
* There is no option to activate monkey-patching.
|
25
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.
|
26
|
-
*
|
27
|
+
* Malicious _actual values_ cannot [hack results](https://asciinema.org/a/423547?autoplay=1&speed=2).
|
27
28
|
* If no `subject` has been explicitly determined, none is defined.
|
28
29
|
* If no described class is set, `described_class` is undefined instead of `nil`.
|
29
30
|
* Expectations cannot be added inside a `before` block.
|
@@ -67,7 +68,7 @@ A basic spec looks something like this:
|
|
67
68
|
|
68
69
|
### Anatomy of a spec file
|
69
70
|
|
70
|
-
To use the `RSpec` module and its DSL, you need to add `require "r_spec
|
71
|
+
To use the `RSpec` module and its DSL, you need to add `require "r_spec"` to your spec files.
|
71
72
|
Many projects use a custom spec helper which organizes these includes.
|
72
73
|
|
73
74
|
Concrete test cases are defined in `it` blocks.
|
@@ -95,12 +96,28 @@ For unit tests, it is recommended to follow the conventions for method names:
|
|
95
96
|
|
96
97
|
To establish certain contexts — think _empty array_ versus _array with elements_ — the `context` method may be used to communicate this to the reader.
|
97
98
|
|
98
|
-
|
99
|
+
To execute unit tests while isolating side effects in a sub-process, declined methods can be used: `describe!`, `context!`, `it!`, `its!`. Here is an example:
|
99
100
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
101
|
+
```ruby
|
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
121
|
|
105
122
|
### Expectations
|
106
123
|
|
@@ -221,13 +238,13 @@ bundle exec rake
|
|
221
238
|
|
222
239
|
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).
|
223
240
|
|
224
|
-

|
241
|
+

|
225
242
|
|
226
|
-
###
|
243
|
+
### Runtime
|
227
244
|
|
228
245
|
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).
|
229
246
|
|
230
|
-

|
231
248
|
|
232
249
|
## Test suite
|
233
250
|
|
@@ -237,6 +254,7 @@ __RSpec clone__'s specifications are self-described here: [spec/](https://github
|
|
237
254
|
|
238
255
|
* Home page: [https://r-spec.dev/](https://r-spec.dev/)
|
239
256
|
* Cheatsheet: [https://r-spec.dev/cheatsheet.html](https://r-spec.dev/cheatsheet.html)
|
257
|
+
* Blog post: [https://batman.buzz/introducing-a-new-rspec-850d48c0f901](https://batman.buzz/introducing-a-new-rspec-850d48c0f901)
|
240
258
|
* Source code: [https://github.com/cyril/r_spec-clone.rb](https://github.com/cyril/r_spec-clone.rb)
|
241
259
|
* API Doc: [https://rubydoc.info/gems/r_spec-clone](https://rubydoc.info/gems/r_spec-clone)
|
242
260
|
* Twitter: [https://twitter.com/cyri\_](https://twitter.com/cyri\_)
|
data/lib/r_spec.rb
CHANGED
@@ -5,7 +5,7 @@ require_relative File.join("r_spec", "clone", "dsl")
|
|
5
5
|
# Top level namespace for the RSpec clone.
|
6
6
|
#
|
7
7
|
# @example The true from the false
|
8
|
-
# require "r_spec
|
8
|
+
# require "r_spec"
|
9
9
|
#
|
10
10
|
# RSpec.describe "The true from the false" do
|
11
11
|
# it { expect(false).not_to be true }
|
@@ -15,7 +15,7 @@ require_relative File.join("r_spec", "clone", "dsl")
|
|
15
15
|
# # Success: expected false not to be true.
|
16
16
|
#
|
17
17
|
# @example The basic behavior of arrays
|
18
|
-
# require "r_spec
|
18
|
+
# require "r_spec"
|
19
19
|
#
|
20
20
|
# RSpec.describe Array do
|
21
21
|
# describe "#size" do
|
@@ -41,7 +41,7 @@ require_relative File.join("r_spec", "clone", "dsl")
|
|
41
41
|
# # Success: expected false to be false.
|
42
42
|
#
|
43
43
|
# @example An inherited definition of let
|
44
|
-
# require "r_spec
|
44
|
+
# require "r_spec"
|
45
45
|
#
|
46
46
|
# RSpec.describe Integer do
|
47
47
|
# let(:answer) { 42 }
|
@@ -69,7 +69,7 @@ module RSpec
|
|
69
69
|
# array_ versus _array with elements_.
|
70
70
|
#
|
71
71
|
# @example
|
72
|
-
# require "r_spec
|
72
|
+
# require "r_spec"
|
73
73
|
#
|
74
74
|
# RSpec.context "when divided by zero" do
|
75
75
|
# subject { 42 / 0 }
|
@@ -83,30 +83,62 @@ module RSpec
|
|
83
83
|
# @param description [String] A description that usually begins with "when",
|
84
84
|
# "with" or "without".
|
85
85
|
# @param block [Proc] The block to define the specs.
|
86
|
-
#
|
87
|
-
# @api public
|
88
86
|
def self.context(description, &block)
|
89
87
|
Clone::Dsl.context(description, &block)
|
90
88
|
end
|
91
89
|
|
92
90
|
# :nocov:
|
93
|
-
|
91
|
+
|
94
92
|
# Runs a context example group in a subprocess to isolate side effects.
|
95
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
|
+
#
|
96
115
|
# @param (see #context)
|
97
116
|
def self.context!(description, &block)
|
98
117
|
Clone::Dsl.context!(description, &block)
|
99
118
|
end
|
119
|
+
|
100
120
|
# :nocov:
|
101
121
|
|
102
122
|
# Defines an example group that describes a unit to be tested.
|
103
123
|
#
|
104
124
|
# @example
|
105
|
-
# require "r_spec
|
125
|
+
# require "r_spec"
|
126
|
+
#
|
127
|
+
# RSpec.describe String do
|
128
|
+
# it { expect(described_class).to be String }
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# # Output to the console
|
132
|
+
# # Success: expected to be String.
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
# require "r_spec"
|
106
136
|
#
|
107
137
|
# RSpec.describe String do
|
138
|
+
# let(:foo) { "foo" }
|
139
|
+
#
|
108
140
|
# describe "+" do
|
109
|
-
# it("concats") { expect(
|
141
|
+
# it("concats") { expect(foo + "bar").to eq "foobar" }
|
110
142
|
# end
|
111
143
|
# end
|
112
144
|
#
|
@@ -115,20 +147,40 @@ module RSpec
|
|
115
147
|
#
|
116
148
|
# @param const [Module, String] A module to include in block context.
|
117
149
|
# @param block [Proc] The block to define the specs.
|
118
|
-
#
|
119
|
-
# @api public
|
120
150
|
def self.describe(const, &block)
|
121
151
|
Clone::Dsl.describe(const, &block)
|
122
152
|
end
|
123
153
|
|
124
154
|
# :nocov:
|
125
|
-
|
155
|
+
|
126
156
|
# Runs a describe example group in a subprocess to isolate side effects.
|
127
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
|
+
#
|
128
179
|
# @param (see #describe)
|
129
180
|
def self.describe!(const, &block)
|
130
181
|
Clone::Dsl.describe!(const, &block)
|
131
182
|
end
|
183
|
+
|
132
184
|
# :nocov:
|
133
185
|
|
134
186
|
# Defines a concrete test case.
|
@@ -136,7 +188,7 @@ module RSpec
|
|
136
188
|
# The test is performed by the block supplied to &block.
|
137
189
|
#
|
138
190
|
# @example The integer after 41
|
139
|
-
# require "r_spec
|
191
|
+
# require "r_spec"
|
140
192
|
#
|
141
193
|
# RSpec.it { expect(41.next).to be 42 }
|
142
194
|
#
|
@@ -149,21 +201,21 @@ module RSpec
|
|
149
201
|
# @param name [String, nil] The name of the spec.
|
150
202
|
# @param block [Proc] An expectation to evaluate.
|
151
203
|
#
|
152
|
-
# @raise (see RSpec::ExpectationTarget::Base#result)
|
153
|
-
# @return (see RSpec::ExpectationTarget::Base#result)
|
154
|
-
#
|
155
|
-
# @api public
|
204
|
+
# @raise (see RSpec::Clone::ExpectationTarget::Base#result)
|
205
|
+
# @return (see RSpec::Clone::ExpectationTarget::Base#result)
|
156
206
|
def self.it(name = nil, &block)
|
157
207
|
Clone::Dsl.it(name, &block)
|
158
208
|
end
|
159
209
|
|
160
210
|
# :nocov:
|
161
|
-
|
211
|
+
|
162
212
|
# Runs a concrete test case in a subprocess to isolate side effects.
|
163
213
|
#
|
164
214
|
# @example
|
165
215
|
# app = "Hello, world!"
|
166
216
|
#
|
217
|
+
# require "r_spec"
|
218
|
+
#
|
167
219
|
# RSpec.it! { expect(app.gsub!("world", "Alice")).to eq "Hello, Alice!" }
|
168
220
|
#
|
169
221
|
# # Output to the console
|
@@ -181,6 +233,7 @@ module RSpec
|
|
181
233
|
def self.it!(name = nil, &block)
|
182
234
|
Clone::Dsl.it!(name, &block)
|
183
235
|
end
|
236
|
+
|
184
237
|
# :nocov:
|
185
238
|
|
186
239
|
# Defines a pending test case.
|
@@ -189,7 +242,7 @@ module RSpec
|
|
189
242
|
# not yet implemented.
|
190
243
|
#
|
191
244
|
# @example
|
192
|
-
# require "r_spec
|
245
|
+
# require "r_spec"
|
193
246
|
#
|
194
247
|
# RSpec.pending "is implemented but waiting" do
|
195
248
|
# expect something to be finished
|
@@ -207,8 +260,6 @@ module RSpec
|
|
207
260
|
# @param message [String] The reason why the example is pending.
|
208
261
|
#
|
209
262
|
# @return [nil] Write a message to STDOUT.
|
210
|
-
#
|
211
|
-
# @api public
|
212
263
|
def self.pending(message)
|
213
264
|
Clone::Dsl.pending(message)
|
214
265
|
end
|
data/lib/r_spec/clone/console.rb
CHANGED
data/lib/r_spec/clone/dsl.rb
CHANGED
@@ -16,7 +16,7 @@ module RSpec
|
|
16
16
|
# Executes the given block before each spec in the current context runs.
|
17
17
|
#
|
18
18
|
# @example
|
19
|
-
# require "r_spec
|
19
|
+
# require "r_spec"
|
20
20
|
#
|
21
21
|
# RSpec.describe Integer do
|
22
22
|
# before do
|
@@ -42,6 +42,8 @@ module RSpec
|
|
42
42
|
# # Success: expected to be 123.
|
43
43
|
#
|
44
44
|
# @param block [Proc] The content to execute at the class initialization.
|
45
|
+
#
|
46
|
+
# @api public
|
45
47
|
def self.before(&block)
|
46
48
|
define_method(BEFORE_METHOD) do
|
47
49
|
super()
|
@@ -54,7 +56,7 @@ module RSpec
|
|
54
56
|
# Executes the given block after each spec in the current context runs.
|
55
57
|
#
|
56
58
|
# @example
|
57
|
-
# require "r_spec
|
59
|
+
# require "r_spec"
|
58
60
|
#
|
59
61
|
# RSpec.describe Integer do
|
60
62
|
# after do
|
@@ -69,6 +71,8 @@ module RSpec
|
|
69
71
|
# # That is the answer to everything.
|
70
72
|
#
|
71
73
|
# @param block [Proc] The content to execute at the class initialization.
|
74
|
+
#
|
75
|
+
# @api public
|
72
76
|
def self.after(&block)
|
73
77
|
define_method(AFTER_METHOD) do
|
74
78
|
instance_exec(&block)
|
@@ -81,7 +85,7 @@ module RSpec
|
|
81
85
|
# Sets a user-defined property.
|
82
86
|
#
|
83
87
|
# @example
|
84
|
-
# require "r_spec
|
88
|
+
# require "r_spec"
|
85
89
|
#
|
86
90
|
# RSpec.describe "Name stories" do
|
87
91
|
# let(:name) { "Bob" }
|
@@ -103,6 +107,8 @@ module RSpec
|
|
103
107
|
# @param block [Proc] The content of the method to define.
|
104
108
|
#
|
105
109
|
# @return [Symbol] A private method that define the block content.
|
110
|
+
#
|
111
|
+
# @api public
|
106
112
|
def self.let(name, *args, **kwargs, &block)
|
107
113
|
raise Error::ReservedMethod if [BEFORE_METHOD, AFTER_METHOD].include?(name.to_sym)
|
108
114
|
|
@@ -112,7 +118,7 @@ module RSpec
|
|
112
118
|
# Sets a user-defined property named {#subject}.
|
113
119
|
#
|
114
120
|
# @example
|
115
|
-
# require "r_spec
|
121
|
+
# require "r_spec"
|
116
122
|
#
|
117
123
|
# RSpec.describe Array do
|
118
124
|
# subject { [1, 2, 3] }
|
@@ -120,13 +126,23 @@ module RSpec
|
|
120
126
|
# it "has the prescribed elements" do
|
121
127
|
# expect(subject).to eq([1, 2, 3])
|
122
128
|
# end
|
129
|
+
#
|
130
|
+
# it { is_expected.to be_an_instance_of described_class }
|
131
|
+
#
|
132
|
+
# its(:size) { is_expected.to be 3 }
|
133
|
+
# its(:downcase) { is_expected.to raise_exception NoMethodError }
|
123
134
|
# end
|
124
135
|
#
|
125
136
|
# # Output to the console
|
126
137
|
# # Success: expected to eq [1, 2, 3].
|
138
|
+
# # Success: expected [1, 2, 3] to be an instance of Array.
|
139
|
+
# # Success: expected to be 3.
|
140
|
+
# # Success: undefined method `downcase' for [1, 2, 3]:Array.
|
127
141
|
#
|
128
142
|
# @param block [Proc] The subject to set.
|
129
143
|
# @return [Symbol] A {#subject} method that define the block content.
|
144
|
+
#
|
145
|
+
# @api public
|
130
146
|
def self.subject(&block)
|
131
147
|
let(__method__, &block)
|
132
148
|
end
|
@@ -134,7 +150,7 @@ module RSpec
|
|
134
150
|
# Defines an example group that describes a unit to be tested.
|
135
151
|
#
|
136
152
|
# @example
|
137
|
-
# require "r_spec
|
153
|
+
# require "r_spec"
|
138
154
|
#
|
139
155
|
# RSpec.describe String do
|
140
156
|
# describe "+" do
|
@@ -147,6 +163,8 @@ module RSpec
|
|
147
163
|
#
|
148
164
|
# @param const [Module, String] A module to include in block context.
|
149
165
|
# @param block [Proc] The block to define the specs.
|
166
|
+
#
|
167
|
+
# @api public
|
150
168
|
def self.describe(const, &block)
|
151
169
|
desc = ::Class.new(self)
|
152
170
|
desc.let(:described_class) { const } if const.is_a?(::Module)
|
@@ -154,20 +172,53 @@ module RSpec
|
|
154
172
|
end
|
155
173
|
|
156
174
|
# :nocov:
|
157
|
-
|
175
|
+
|
158
176
|
# Runs a describe example group in a subprocess to isolate side effects.
|
159
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
|
+
#
|
160
208
|
# @param (see #describe)
|
209
|
+
#
|
210
|
+
# @api public
|
161
211
|
def self.describe!(const, &block)
|
162
212
|
fork! { describe(const, &block) }
|
163
213
|
end
|
214
|
+
|
164
215
|
# :nocov:
|
165
216
|
|
166
217
|
# Defines an example group that establishes a specific context, like
|
167
218
|
# _empty array_ versus _array with elements_.
|
168
219
|
#
|
169
220
|
# @example
|
170
|
-
# require "r_spec
|
221
|
+
# require "r_spec"
|
171
222
|
#
|
172
223
|
# RSpec.describe "web resource" do
|
173
224
|
# context "when resource is not found" do
|
@@ -186,19 +237,55 @@ module RSpec
|
|
186
237
|
# @param _description [String] A description that usually begins with
|
187
238
|
# "when", "with" or "without".
|
188
239
|
# @param block [Proc] The block to define the specs.
|
240
|
+
#
|
241
|
+
# @api public
|
189
242
|
def self.context(_description, &block)
|
190
243
|
desc = ::Class.new(self)
|
191
244
|
desc.instance_eval(&block)
|
192
245
|
end
|
193
246
|
|
194
247
|
# :nocov:
|
195
|
-
|
248
|
+
|
196
249
|
# Runs a context example group in a subprocess to isolate side effects.
|
197
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
|
+
#
|
198
282
|
# @param (see #context)
|
283
|
+
#
|
284
|
+
# @api public
|
199
285
|
def self.context!(description, &block)
|
200
286
|
fork! { context(description, &block) }
|
201
287
|
end
|
288
|
+
|
202
289
|
# :nocov:
|
203
290
|
|
204
291
|
# Defines a concrete test case.
|
@@ -206,7 +293,7 @@ module RSpec
|
|
206
293
|
# The test is performed by the block supplied to `&block`.
|
207
294
|
#
|
208
295
|
# @example The integer after 41
|
209
|
-
# require "r_spec
|
296
|
+
# require "r_spec"
|
210
297
|
#
|
211
298
|
# RSpec.describe Integer do
|
212
299
|
# it { expect(41.next).to be 42 }
|
@@ -216,7 +303,7 @@ module RSpec
|
|
216
303
|
# # Success: expected to be 42.
|
217
304
|
#
|
218
305
|
# @example A division by zero
|
219
|
-
# require "r_spec
|
306
|
+
# require "r_spec"
|
220
307
|
#
|
221
308
|
# RSpec.describe Integer do
|
222
309
|
# subject { 41 }
|
@@ -239,29 +326,54 @@ module RSpec
|
|
239
326
|
#
|
240
327
|
# @raise (see ExpectationTarget::Base#result)
|
241
328
|
# @return (see ExpectationTarget::Base#result)
|
329
|
+
#
|
330
|
+
# @api public
|
242
331
|
def self.it(_name = nil, &block)
|
243
|
-
|
244
|
-
run(example, &block)
|
332
|
+
run(example_without_attribute.new, &block)
|
245
333
|
end
|
246
334
|
|
247
335
|
# :nocov:
|
248
|
-
|
336
|
+
|
249
337
|
# Runs a concrete test case in a subprocess to isolate side effects.
|
250
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
|
+
#
|
251
360
|
# @param (see #it)
|
252
361
|
#
|
253
362
|
# @raise (see ExpectationTarget::Base#result)
|
254
363
|
# @return (see ExpectationTarget::Base#result)
|
364
|
+
#
|
365
|
+
# @api public
|
255
366
|
def self.it!(name = nil, &block)
|
256
367
|
fork! { it(name, &block) }
|
257
368
|
end
|
369
|
+
|
258
370
|
# :nocov:
|
259
371
|
|
260
372
|
# Defines a single concrete test case that specifies the actual value of
|
261
373
|
# an attribute of the subject using {ExpectationHelper::Its#is_expected}.
|
262
374
|
#
|
263
375
|
# @example The integer after 41
|
264
|
-
# require "r_spec
|
376
|
+
# require "r_spec"
|
265
377
|
#
|
266
378
|
# RSpec.describe Integer do
|
267
379
|
# subject { 41 }
|
@@ -273,7 +385,7 @@ module RSpec
|
|
273
385
|
# # Success: expected to be 42.
|
274
386
|
#
|
275
387
|
# @example A division by zero
|
276
|
-
# require "r_spec
|
388
|
+
# require "r_spec"
|
277
389
|
#
|
278
390
|
# RSpec.describe Integer do
|
279
391
|
# subject { 41 }
|
@@ -285,10 +397,10 @@ module RSpec
|
|
285
397
|
# # Success: divided by 0.
|
286
398
|
#
|
287
399
|
# @example A spec without subject
|
288
|
-
# require "r_spec
|
400
|
+
# require "r_spec"
|
289
401
|
#
|
290
402
|
# RSpec.describe Integer do
|
291
|
-
# its(:
|
403
|
+
# its(:abs) { is_expected.to raise_exception RSpec::Clone::Error::UndefinedSubject }
|
292
404
|
# end
|
293
405
|
#
|
294
406
|
# # Output to the console
|
@@ -301,30 +413,48 @@ module RSpec
|
|
301
413
|
#
|
302
414
|
# @raise (see ExpectationTarget::Base#result)
|
303
415
|
# @return (see ExpectationTarget::Base#result)
|
416
|
+
#
|
417
|
+
# @api public
|
304
418
|
def self.its(attribute, *args, **kwargs, &block)
|
305
|
-
|
306
|
-
include ExpectationHelper::Its
|
307
|
-
|
308
|
-
define_method(:actual) do
|
309
|
-
subject.public_send(attribute, *args, **kwargs)
|
310
|
-
end
|
311
|
-
end.new
|
312
|
-
|
313
|
-
run(example, &block)
|
419
|
+
run(example_with_attribute(attribute, *args, **kwargs).new, &block)
|
314
420
|
end
|
315
421
|
|
316
422
|
# :nocov:
|
317
|
-
|
423
|
+
|
318
424
|
# Runs a single concrete test case in a subprocess to isolate side
|
319
425
|
# effects.
|
320
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
|
+
#
|
321
448
|
# @param (see #it)
|
322
449
|
#
|
323
450
|
# @raise (see ExpectationTarget::Base#result)
|
324
451
|
# @return (see ExpectationTarget::Base#result)
|
452
|
+
#
|
453
|
+
# @api public
|
325
454
|
def self.its!(attribute, *args, **kwargs, &block)
|
326
455
|
fork! { its(attribute, *args, **kwargs, &block) }
|
327
456
|
end
|
457
|
+
|
328
458
|
# :nocov:
|
329
459
|
|
330
460
|
# Defines a pending test case.
|
@@ -333,7 +463,7 @@ module RSpec
|
|
333
463
|
# is not yet implemented.
|
334
464
|
#
|
335
465
|
# @example
|
336
|
-
# require "r_spec
|
466
|
+
# require "r_spec"
|
337
467
|
#
|
338
468
|
# RSpec.describe "an example" do
|
339
469
|
# pending "is implemented but waiting" do
|
@@ -356,6 +486,25 @@ module RSpec
|
|
356
486
|
Console.passed_spec Error::PendingExpectation.result(message)
|
357
487
|
end
|
358
488
|
|
489
|
+
# Example class for concrete test case.
|
490
|
+
def self.example_without_attribute
|
491
|
+
::Class.new(self) do
|
492
|
+
prepend ExpectationHelper::It
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
# Example class for concrete test case that specifies the actual value of
|
497
|
+
# an attribute of the subject.
|
498
|
+
def self.example_with_attribute(attribute, *args, **kwargs)
|
499
|
+
::Class.new(self) do
|
500
|
+
prepend ExpectationHelper::Its
|
501
|
+
|
502
|
+
define_method(:actual) do
|
503
|
+
subject.public_send(attribute, *args, **kwargs)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
359
508
|
# Creates a subprocess and runs the block inside.
|
360
509
|
def self.fork!(&block)
|
361
510
|
pid = fork(&block)
|
@@ -375,15 +524,18 @@ module RSpec
|
|
375
524
|
example&.send(AFTER_METHOD)
|
376
525
|
end
|
377
526
|
|
378
|
-
private_class_method :fork!, :run
|
527
|
+
private_class_method :example_without_attribute, :example_with_attribute, :fork!, :run
|
379
528
|
|
380
529
|
private
|
381
530
|
|
531
|
+
# If the first argument to a {.describe} definition is a class (or a
|
532
|
+
# module), this method will be overridden to return it.
|
382
533
|
def described_class
|
383
534
|
raise Error::UndefinedDescribedClass,
|
384
535
|
"the first argument to at least one example group must be a module"
|
385
536
|
end
|
386
537
|
|
538
|
+
# If a subject is defined, this method will be overridden to return it.
|
387
539
|
def subject
|
388
540
|
raise Error::UndefinedSubject, "subject not explicitly defined"
|
389
541
|
end
|
data/lib/r_spec/clone/error.rb
CHANGED
@@ -18,8 +18,8 @@ module RSpec
|
|
18
18
|
# @return [Block, Value] The wrapped target of an expectation.
|
19
19
|
#
|
20
20
|
# @example
|
21
|
-
# expect("foo") # => #<RSpec::ExpectationTarget::Value:
|
22
|
-
# expect { Boom } # => #<RSpec::ExpectationTarget::Block:
|
21
|
+
# expect("foo") # => #<RSpec::Clone::ExpectationTarget::Value:0x00007f @actual="foo">
|
22
|
+
# expect { Boom } # => #<RSpec::Clone::ExpectationTarget::Block:0x00007f @callable=#<Proc:0x00007f>>
|
23
23
|
#
|
24
24
|
# @api public
|
25
25
|
def expect(value = self.class.superclass, &block)
|
@@ -31,7 +31,7 @@ module RSpec
|
|
31
31
|
# @return [Block] The wrapped target of an expectation.
|
32
32
|
#
|
33
33
|
# @example
|
34
|
-
# is_expected # => #<RSpec::ExpectationTarget::Block:
|
34
|
+
# is_expected # => #<RSpec::Clone::ExpectationTarget::Block:0x00007fb6b8 @callable=#<Proc:0x00007fb6b8>>
|
35
35
|
#
|
36
36
|
# @api public
|
37
37
|
def is_expected
|
@@ -15,7 +15,7 @@ module RSpec
|
|
15
15
|
# @return [Block] The wrapped target of an expectation.
|
16
16
|
#
|
17
17
|
# @example
|
18
|
-
# is_expected # => #<RSpec::ExpectationTarget::Block:
|
18
|
+
# is_expected # => #<RSpec::Clone::ExpectationTarget::Block:0x00007f @callable=#<Proc:0x00007f>>
|
19
19
|
#
|
20
20
|
# @api public
|
21
21
|
def is_expected
|
@@ -1,16 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "matchi/rspec"
|
4
|
-
|
5
|
-
require_relative File.join("..", "error", "pending_expectation")
|
4
|
+
require "matchi/helper"
|
6
5
|
|
7
6
|
module RSpec
|
8
7
|
module Clone
|
9
8
|
module ExpectationHelper
|
10
9
|
# Abstract expectation helper base module.
|
11
10
|
#
|
12
|
-
# This module defines a number of methods to create expectations, which
|
13
|
-
# automatically included into examples.
|
11
|
+
# This module defines a number of methods to create expectations, which
|
12
|
+
# are automatically included into examples.
|
14
13
|
#
|
15
14
|
# It also includes a collection of expectation matchers 🤹
|
16
15
|
#
|
@@ -6,12 +6,10 @@ require_relative File.join("expectation_target", "value")
|
|
6
6
|
module RSpec
|
7
7
|
module Clone
|
8
8
|
# Wraps the target of an expectation.
|
9
|
-
#
|
10
|
-
# @api private
|
11
9
|
module ExpectationTarget
|
12
10
|
# @param undefined_value A sentinel value to be able to tell when the user
|
13
|
-
# did not pass an argument. We can't use `nil` for that because `nil` is
|
14
|
-
# valid value to pass.
|
11
|
+
# did not pass an argument. We can't use `nil` for that because `nil` is
|
12
|
+
# a valid value to pass.
|
15
13
|
# @param value [#object_id, nil] An actual value.
|
16
14
|
# @param block [#call, nil] A code to evaluate.
|
17
15
|
#
|
@@ -10,14 +10,14 @@ module RSpec
|
|
10
10
|
module ExpectationTarget
|
11
11
|
# Abstract expectation target base class.
|
12
12
|
#
|
13
|
-
# @note `RSpec::ExpectationTarget::Base` is not intended to be
|
14
|
-
# directly by users. Use `expect` instead.
|
13
|
+
# @note `RSpec::Clone::ExpectationTarget::Base` is not intended to be
|
14
|
+
# instantiated directly by users. Use `expect` instead.
|
15
15
|
class Base
|
16
16
|
# Instantiate a new expectation target.
|
17
17
|
#
|
18
|
-
# @param
|
19
|
-
def initialize(
|
20
|
-
@
|
18
|
+
# @param input [#object_id, Proc] The code to evaluate.
|
19
|
+
def initialize(input)
|
20
|
+
@input = input
|
21
21
|
end
|
22
22
|
|
23
23
|
# Runs the given expectation, passing if `matcher` returns true.
|
@@ -52,6 +52,36 @@ module RSpec
|
|
52
52
|
|
53
53
|
protected
|
54
54
|
|
55
|
+
# @param test [::TestTube::Base] The state of the experiment.
|
56
|
+
# @param matcher [#matches?] The matcher.
|
57
|
+
# @param negate [Boolean] The assertion is positive or negative.
|
58
|
+
#
|
59
|
+
# @return [nil] Write a message to STDOUT.
|
60
|
+
#
|
61
|
+
# @raise [SystemExit] Terminate execution immediately by calling
|
62
|
+
# `Kernel.exit(false)` with a failure message written to STDERR.
|
63
|
+
def absolute_requirement(test, matcher:, negate:)
|
64
|
+
result(
|
65
|
+
passed?(test),
|
66
|
+
actual: test.actual,
|
67
|
+
error: test.error,
|
68
|
+
got: test.got,
|
69
|
+
matcher: matcher,
|
70
|
+
negate: negate
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Code experiment result.
|
75
|
+
#
|
76
|
+
# @param test [::TestTube::Base] The state of the experiment.
|
77
|
+
#
|
78
|
+
# @see https://github.com/fixrb/test_tube
|
79
|
+
#
|
80
|
+
# @return [Boolean] The result of the test (passed or failed).
|
81
|
+
def passed?(test)
|
82
|
+
test.got.equal?(true)
|
83
|
+
end
|
84
|
+
|
55
85
|
# @param passed [Boolean] The high expectation passed or failed.
|
56
86
|
# @param actual [#object_id] The actual value.
|
57
87
|
# @param error [Exception, nil] Any raised exception.
|
@@ -63,8 +93,6 @@ module RSpec
|
|
63
93
|
#
|
64
94
|
# @raise [SystemExit] Terminate execution immediately by calling
|
65
95
|
# `Kernel.exit(false)` with a failure message written to STDERR.
|
66
|
-
#
|
67
|
-
# @api private
|
68
96
|
def result(passed, actual:, error:, got:, matcher:, negate:)
|
69
97
|
Console.passed_spec ::Expresenter.call(passed).with(
|
70
98
|
actual: actual,
|
@@ -16,31 +16,20 @@ module RSpec
|
|
16
16
|
# # with `not_to`
|
17
17
|
# expect { actual }.not_to be(4)
|
18
18
|
#
|
19
|
-
# @note `RSpec::ExpectationTarget::Block` is not intended to be
|
20
|
-
# directly by users. Use `expect` instead.
|
19
|
+
# @note `RSpec::Clone::ExpectationTarget::Block` is not intended to be
|
20
|
+
# instantiated directly by users. Use `expect` instead.
|
21
21
|
class Block < Base
|
22
22
|
protected
|
23
23
|
|
24
24
|
# @param matcher [#matches?] The matcher.
|
25
25
|
# @param negate [Boolean] The assertion is positive or negative.
|
26
26
|
#
|
27
|
-
# @return
|
27
|
+
# @return (see Base#absolute_requirement)
|
28
28
|
#
|
29
|
-
# @raise
|
30
|
-
# `Kernel.exit(false)` with a failure message written to STDERR.
|
29
|
+
# @raise (see Base#absolute_requirement)
|
31
30
|
def absolute_requirement(matcher:, negate:)
|
32
|
-
|
33
|
-
|
34
|
-
isolation: false,
|
35
|
-
matcher: matcher,
|
36
|
-
negate: negate
|
37
|
-
)
|
38
|
-
|
39
|
-
result(
|
40
|
-
experiment.got.equal?(true),
|
41
|
-
actual: experiment.actual,
|
42
|
-
error: experiment.error,
|
43
|
-
got: experiment.got,
|
31
|
+
super(
|
32
|
+
::TestTube.invoke(isolate: false, matcher: matcher, negate: negate, &@input),
|
44
33
|
matcher: matcher,
|
45
34
|
negate: negate
|
46
35
|
)
|
@@ -16,30 +16,20 @@ module RSpec
|
|
16
16
|
# # with `not_to`
|
17
17
|
# expect(actual).not_to be(4)
|
18
18
|
#
|
19
|
-
# @note `RSpec::ExpectationTarget::Value` is not intended to be
|
20
|
-
# directly by users. Use `expect` instead.
|
19
|
+
# @note `RSpec::Clone::ExpectationTarget::Value` is not intended to be
|
20
|
+
# instantiated directly by users. Use `expect` instead.
|
21
21
|
class Value < Base
|
22
22
|
protected
|
23
23
|
|
24
24
|
# @param matcher [#matches?] The matcher.
|
25
25
|
# @param negate [Boolean] The assertion is positive or negative.
|
26
26
|
#
|
27
|
-
# @return
|
27
|
+
# @return (see Base#absolute_requirement)
|
28
28
|
#
|
29
|
-
# @raise
|
30
|
-
# `Kernel.exit(false)` with a failure message written to STDERR.
|
29
|
+
# @raise (see Base#absolute_requirement)
|
31
30
|
def absolute_requirement(matcher:, negate:)
|
32
|
-
|
33
|
-
@
|
34
|
-
matcher: matcher,
|
35
|
-
negate: negate
|
36
|
-
)
|
37
|
-
|
38
|
-
result(
|
39
|
-
experiment.got.equal?(true),
|
40
|
-
actual: experiment.actual,
|
41
|
-
error: experiment.error,
|
42
|
-
got: experiment.got,
|
31
|
+
super(
|
32
|
+
::TestTube.pass(@input, matcher: matcher, negate: negate),
|
43
33
|
matcher: matcher,
|
44
34
|
negate: negate
|
45
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.
|
4
|
+
version: 1.2.4
|
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-
|
11
|
+
date: 2021-07-19 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: 1.
|
33
|
+
version: 1.2.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.
|
40
|
+
version: 1.2.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:
|
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:
|
54
|
+
version: 2.1.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|