r_spec 1.0.0.beta4 → 1.0.0.beta9
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 +37 -17
- data/lib/r_spec.rb +16 -4
- data/lib/r_spec/console.rb +38 -0
- data/lib/r_spec/dsl.rb +268 -58
- data/lib/r_spec/error.rb +14 -0
- data/lib/r_spec/error/pending_expectation.rb +27 -0
- data/lib/r_spec/error/reserved_method.rb +11 -0
- data/lib/r_spec/error/undefined_described_class.rb +11 -0
- data/lib/r_spec/error/undefined_subject.rb +11 -0
- data/lib/r_spec/expectation_helper.rb +4 -4
- data/lib/r_spec/expectation_helper/it.rb +4 -4
- data/lib/r_spec/expectation_helper/its.rb +3 -3
- data/lib/r_spec/expectation_helper/{base.rb → shared.rb} +3 -17
- data/lib/r_spec/expectation_target.rb +3 -3
- data/lib/r_spec/expectation_target/base.rb +15 -6
- data/lib/r_spec/expectation_target/block.rb +9 -20
- data/lib/r_spec/expectation_target/value.rb +10 -15
- metadata +16 -12
- data/lib/r_spec/pending.rb +0 -26
- data/lib/r_spec/sandbox.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6599635e0de35bec4d9bae9f58ab1417a364eb2a0bd64db49b523be4d9ab1d16
|
4
|
+
data.tar.gz: 658671a666d8173ce075558dc7dc0f5604e2a98f86e0448485c2ad713ec518be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45babe5acbed804ce1a61476c39028cdaa46256c343b0b6001b83e0bd5cda35a83770bd06776c7b76d22cb0dd3cbc07be7978d30d99c04cc1585e9cdeb933778
|
7
|
+
data.tar.gz: 78f72312fc8c2ad764647a1e6698f4a094bfc3129c0b2911f809502ba9a11d9a0da88ffafc2420c68d55fdaeeb8062258e8ff738013bded1bcf1970f536f192d
|
data/README.md
CHANGED
@@ -2,18 +2,18 @@
|
|
2
2
|
|
3
3
|
A minimalist __[RSpec](https://github.com/rspec/rspec) clone__ with all the essentials.
|
4
4
|
|
5
|
-

|
6
6
|
|
7
7
|
## Status
|
8
8
|
|
9
9
|
[](https://badge.fury.io/rb/r_spec)
|
10
10
|
[](https://travis-ci.org/cyril/r_spec.rb)
|
11
|
-
[](https://inch-ci.org/github/cyril/r_spec.rb)
|
12
11
|
[](https://rubydoc.info/gems/r_spec/frames)
|
13
12
|
|
14
|
-
##
|
13
|
+
## Project goals
|
15
14
|
|
16
|
-
|
15
|
+
* Enforce the guidelines and best practices outlined in the community [RSpec style guide](https://rspec.rubystyle.guide/).
|
16
|
+
* Provide most of RSpec's DSL to express expected outcomes of a code example without magic power.
|
17
17
|
|
18
18
|
## Some differences
|
19
19
|
|
@@ -22,11 +22,13 @@ This clone attempts to provide most of RSpec's DSL to express expected outcomes
|
|
22
22
|
* There is no option to activate monkey-patching.
|
23
23
|
* It does not rely on hacks such as `at_exit` hook to trigger the tests.
|
24
24
|
* Built-in matchers do not trust _actual_ and do not send it messages.
|
25
|
-
*
|
25
|
+
* If no `subject` has been explicitly determined, none is defined.
|
26
|
+
* If no described class is set, `described_class` is undefined instead of `nil`.
|
26
27
|
* Expectations cannot be added inside a `before` block.
|
27
28
|
* [Arbitrary helper methods](https://relishapp.com/rspec/rspec-core/v/3-10/docs/helper-methods/arbitrary-helper-methods) are not exposed to examples.
|
28
29
|
* The `let` method defines a helper method rather than a memoized helper method.
|
29
30
|
* The one-liner `is_expected` syntax also works with block expectations.
|
31
|
+
* `subject`, `before`, `after` and `let` definitions must come before examples.
|
30
32
|
|
31
33
|
## Important ⚠️
|
32
34
|
|
@@ -45,7 +47,7 @@ Following [RubyGems naming conventions](https://guides.rubygems.org/name-your-ge
|
|
45
47
|
Add this line to your application's Gemfile:
|
46
48
|
|
47
49
|
```ruby
|
48
|
-
gem "r_spec", ">= 1.0.0.
|
50
|
+
gem "r_spec", ">= 1.0.0.beta9"
|
49
51
|
```
|
50
52
|
|
51
53
|
And then execute:
|
@@ -80,16 +82,15 @@ Many projects use a custom spec helper which organizes these includes.
|
|
80
82
|
Concrete test cases are defined in `it` blocks.
|
81
83
|
An optional descriptive string states it's purpose and a block contains the main logic performing the test.
|
82
84
|
|
85
|
+
Test cases that have been defined or outlined but are not yet expected to work can be defined using `pending` instead of `it`. They will not be run but show up in the spec report as pending.
|
86
|
+
|
83
87
|
An `it` block contains an example that should invoke the code to be tested and define what is expected of it.
|
84
88
|
Each example can contain multiple expectations, but it should test only one specific behaviour.
|
85
89
|
|
86
|
-
To express an expectation, wrap an object or block in `expect`, call `to` or `not_to` and pass it a matcher object.
|
87
|
-
|
90
|
+
To express an expectation, wrap an object or block in `expect`, call `to` (or `not_to`) and pass it a matcher object.
|
88
91
|
If the expectation is met, code execution continues.
|
89
92
|
Otherwise the example has _failed_ and other code will not be executed.
|
90
93
|
|
91
|
-
Test cases that have been defined or outlined but are not yet expected to work can be defined using `pending` instead of `expect`. They will not be run but show up in the spec report as pending.
|
92
|
-
|
93
94
|
In test files, specs are structured by example groups which are defined by `describe` and `context` sections.
|
94
95
|
Typically a top level `describe` defines the outer unit (such as a class) to be tested by the spec.
|
95
96
|
Further `describe` sections can be nested within the outer unit to specify smaller units under test (such as individual methods).
|
@@ -99,7 +100,7 @@ For unit tests, it is recommended to follow the conventions for method names:
|
|
99
100
|
* outer `describe` is the name of the class, inner `describe` targets methods;
|
100
101
|
* instance methods are prefixed with `#`, class methods with `.`.
|
101
102
|
|
102
|
-
To establish certain contexts
|
103
|
+
To establish certain contexts — think _empty array_ versus _array with elements_ — the `context` method may be used to communicate this to the reader.
|
103
104
|
It has a different name, but behaves exactly like `describe`.
|
104
105
|
|
105
106
|
`describe` and `context` take an optional description as argument and a block containing the individual specs or nested groupings.
|
@@ -213,6 +214,19 @@ task spec: :test
|
|
213
214
|
task default: :test
|
214
215
|
```
|
215
216
|
|
217
|
+
## Performance
|
218
|
+
|
219
|
+
### Runtime
|
220
|
+
|
221
|
+
Benchmark against [100 executions of a file containing one expectation](https://github.com/cyril/r_spec.rb/blob/main/benchmark/) (lower is better).
|
222
|
+
|
223
|
+
| Framework | Seconds to complete |
|
224
|
+
|-------------|---------------------|
|
225
|
+
| `r_spec` | 13.0 |
|
226
|
+
| `rspec` | 32.2 |
|
227
|
+
| `minitest` | 17.5 |
|
228
|
+
| `test-unit` | 20.5 |
|
229
|
+
|
216
230
|
## Test suite
|
217
231
|
|
218
232
|
__RSpec clone__'s specifications are self-described here: [spec/](https://github.com/cyril/r_spec.rb/blob/main/spec/)
|
@@ -223,16 +237,22 @@ __RSpec clone__'s specifications are self-described here: [spec/](https://github
|
|
223
237
|
* Source code: https://github.com/cyril/r_spec.rb
|
224
238
|
* Twitter: [https://twitter.com/cyri\_](https://twitter.com/cyri\_)
|
225
239
|
|
226
|
-
##
|
227
|
-
|
228
|
-
__RSpec clone__ follows [Semantic Versioning 2.0](https://semver.org/).
|
229
|
-
|
230
|
-
## Special thanks
|
240
|
+
## Special thanks ❤️
|
231
241
|
|
232
242
|
I would like to thank the whole [RSpec team](https://rspec.info/about/) for all their work.
|
233
243
|
It's a great framework and it's a pleasure to work with every day.
|
234
244
|
|
235
|
-
Without RSpec, this clone would not have been possible.
|
245
|
+
Without RSpec, this clone would not have been possible.
|
246
|
+
|
247
|
+
## Buy me a coffee ☕
|
248
|
+
|
249
|
+
If you like this project please consider making a small donation.
|
250
|
+
|
251
|
+
[](https://etherscan.io/address/0x834b5c1feaff5aebf9cd0f25dc38e741d65ab773)
|
252
|
+
|
253
|
+
## Versioning
|
254
|
+
|
255
|
+
__RSpec clone__ follows [Semantic Versioning 2.0](https://semver.org/).
|
236
256
|
|
237
257
|
## License
|
238
258
|
|
data/lib/r_spec.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative File.join("r_spec", "dsl")
|
4
|
+
|
3
5
|
# Top level namespace for the RSpec clone.
|
4
6
|
#
|
5
7
|
# @example The true from the false
|
6
8
|
# require "r_spec"
|
7
9
|
#
|
8
|
-
# RSpec.describe do
|
10
|
+
# RSpec.describe "The true from the false" do
|
9
11
|
# it { expect(false).not_to be true }
|
10
12
|
# end
|
11
13
|
#
|
14
|
+
# # Output to the console
|
15
|
+
# # Success: expected false not to be true.
|
16
|
+
#
|
12
17
|
# @example The basic behavior of arrays
|
13
18
|
# require "r_spec"
|
14
19
|
#
|
@@ -30,6 +35,11 @@
|
|
30
35
|
# end
|
31
36
|
# end
|
32
37
|
#
|
38
|
+
# # Output to the console
|
39
|
+
# # Success: expected to eq 3.
|
40
|
+
# # Success: expected true to be true.
|
41
|
+
# # Success: expected false to be false.
|
42
|
+
#
|
33
43
|
# @example An inherited definition of let
|
34
44
|
# require "r_spec"
|
35
45
|
#
|
@@ -49,6 +59,10 @@
|
|
49
59
|
# end
|
50
60
|
# end
|
51
61
|
#
|
62
|
+
# # Output to the console
|
63
|
+
# # Success: expected to be 42.
|
64
|
+
# # Success: expected to be 43.
|
65
|
+
#
|
52
66
|
# @api public
|
53
67
|
module RSpec
|
54
68
|
# Specs are built with this method.
|
@@ -57,9 +71,7 @@ module RSpec
|
|
57
71
|
# @param block [Proc] The block to define the specs.
|
58
72
|
#
|
59
73
|
# @api public
|
60
|
-
def self.describe(const
|
74
|
+
def self.describe(const, &block)
|
61
75
|
Dsl.describe(const, &block)
|
62
76
|
end
|
63
77
|
end
|
64
|
-
|
65
|
-
require_relative File.join("r_spec", "dsl")
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
# Send log messages to the console.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
module Console
|
8
|
+
# @param report [::Expresenter::Pass] Passed expectation result presenter.
|
9
|
+
#
|
10
|
+
# @see https://github.com/fixrb/expresenter
|
11
|
+
#
|
12
|
+
# @return [nil] Add a colored message to `$stdout`.
|
13
|
+
def self.passed_spec(report)
|
14
|
+
puts report.colored_string
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param report [::Expresenter::Fail] Failed expectation result presenter.
|
18
|
+
#
|
19
|
+
# @see https://github.com/fixrb/expresenter
|
20
|
+
#
|
21
|
+
# @raise [SystemExit] Terminate execution immediately with colored message.
|
22
|
+
def self.failed_spec(report)
|
23
|
+
abort report.colored_string
|
24
|
+
end
|
25
|
+
|
26
|
+
# The Ruby source filename and line number containing this method or nil if
|
27
|
+
# this method was not defined in Ruby (i.e. native).
|
28
|
+
#
|
29
|
+
# @param filename [String, nil] The Ruby source filename.
|
30
|
+
# @param line [Integer, nil] The Ruby source line number.
|
31
|
+
#
|
32
|
+
# @return [String] The Ruby source filename and line number associated with
|
33
|
+
# the evaluated spec.
|
34
|
+
def self.source(filename, line)
|
35
|
+
puts [filename, line].compact.join(":")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/r_spec/dsl.rb
CHANGED
@@ -1,78 +1,243 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative "console"
|
4
|
+
require_relative "error"
|
5
|
+
require_relative "expectation_helper"
|
4
6
|
|
5
7
|
module RSpec
|
6
8
|
# Abstract class for handling the domain-specific language.
|
7
9
|
class Dsl
|
8
|
-
|
9
|
-
|
10
|
+
BEFORE_METHOD = :initialize
|
11
|
+
AFTER_METHOD = :terminate
|
12
|
+
|
13
|
+
private_constant :BEFORE_METHOD, :AFTER_METHOD
|
14
|
+
|
15
|
+
# Executes the given block before each spec in the current context runs.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# require "r_spec"
|
19
|
+
#
|
20
|
+
# RSpec.describe Integer do
|
21
|
+
# before do
|
22
|
+
# @value = 123
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# it { expect(@value).to be 123 }
|
26
|
+
#
|
27
|
+
# describe "nested" do
|
28
|
+
# before do
|
29
|
+
# @value -= 81
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# it { expect(@value).to be 42 }
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# it { expect(@value).to be 123 }
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # Output to the console
|
39
|
+
# # Success: expected to be 123.
|
40
|
+
# # Success: expected to be 42.
|
41
|
+
# # Success: expected to be 123.
|
10
42
|
#
|
11
43
|
# @param block [Proc] The content to execute at the class initialization.
|
12
44
|
def self.before(&block)
|
13
|
-
define_method(
|
45
|
+
define_method(BEFORE_METHOD) do
|
14
46
|
super()
|
15
|
-
|
47
|
+
instance_eval(&block)
|
16
48
|
end
|
49
|
+
|
50
|
+
private BEFORE_METHOD
|
51
|
+
end
|
52
|
+
|
53
|
+
# Executes the given block after each spec in the current context runs.
|
54
|
+
#
|
55
|
+
# @example
|
56
|
+
# require "r_spec"
|
57
|
+
#
|
58
|
+
# RSpec.describe Integer do
|
59
|
+
# after do
|
60
|
+
# puts "That is the answer to everything."
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# it { expect(42).to be 42 }
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# # Output to the console
|
67
|
+
# # Success: expected to be 42.
|
68
|
+
# # That is the answer to everything.
|
69
|
+
#
|
70
|
+
# @param block [Proc] The content to execute at the class initialization.
|
71
|
+
def self.after(&block)
|
72
|
+
define_method(AFTER_METHOD) do
|
73
|
+
instance_exec(&block)
|
74
|
+
super()
|
75
|
+
end
|
76
|
+
|
77
|
+
private AFTER_METHOD
|
17
78
|
end
|
18
79
|
|
19
80
|
# Sets a user-defined property.
|
20
81
|
#
|
82
|
+
# @example
|
83
|
+
# require "r_spec"
|
84
|
+
#
|
85
|
+
# RSpec.describe "Name stories" do
|
86
|
+
# let(:name) { "Bob" }
|
87
|
+
#
|
88
|
+
# it { expect(name).to eq "Bob" }
|
89
|
+
#
|
90
|
+
# context "with last name" do
|
91
|
+
# let(:name) { "#{super()} Smith" }
|
92
|
+
#
|
93
|
+
# it { expect(name).to eq "Bob Smith" }
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# # Output to the console
|
98
|
+
# # Success: expected to eq "Bob".
|
99
|
+
# # Success: expected to eq "Bob Smith".
|
100
|
+
#
|
21
101
|
# @param name [String, Symbol] The name of the property.
|
22
102
|
# @param block [Proc] The content of the method to define.
|
23
103
|
#
|
24
|
-
# @return [Symbol] A
|
104
|
+
# @return [Symbol] A private method that define the block content.
|
25
105
|
def self.let(name, *args, **kwargs, &block)
|
26
|
-
|
106
|
+
raise Error::ReservedMethod if [BEFORE_METHOD, AFTER_METHOD].include?(name.to_sym)
|
107
|
+
|
108
|
+
private define_method(name, *args, **kwargs, &block)
|
27
109
|
end
|
28
110
|
|
29
|
-
# Sets a user-defined property named
|
111
|
+
# Sets a user-defined property named {#subject}.
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# require "r_spec"
|
115
|
+
#
|
116
|
+
# RSpec.describe Array do
|
117
|
+
# subject { [1, 2, 3] }
|
118
|
+
#
|
119
|
+
# it "has the prescribed elements" do
|
120
|
+
# expect(subject).to eq([1, 2, 3])
|
121
|
+
# end
|
122
|
+
# end
|
123
|
+
#
|
124
|
+
# # Output to the console
|
125
|
+
# # Success: expected to eq [1, 2, 3].
|
30
126
|
#
|
31
127
|
# @param block [Proc] The subject to set.
|
32
|
-
# @return [Symbol] A
|
128
|
+
# @return [Symbol] A {#subject} method that define the block content.
|
33
129
|
def self.subject(&block)
|
34
130
|
let(__method__, &block)
|
35
131
|
end
|
36
132
|
|
37
|
-
#
|
133
|
+
# Defines an example group that describes a unit to be tested.
|
134
|
+
#
|
135
|
+
# @example
|
136
|
+
# require "r_spec"
|
137
|
+
#
|
138
|
+
# RSpec.describe String do
|
139
|
+
# describe "+" do
|
140
|
+
# it("concats") { expect("foo" + "bar").to eq "foobar" }
|
141
|
+
# end
|
142
|
+
# end
|
38
143
|
#
|
39
|
-
#
|
144
|
+
# # Output to the console
|
145
|
+
# # Success: expected to eq "foobar".
|
146
|
+
#
|
147
|
+
# @param const [Module, String] A module to include in block context.
|
40
148
|
# @param block [Proc] The block to define the specs.
|
41
|
-
def self.describe(const
|
42
|
-
desc =
|
43
|
-
|
44
|
-
if const.is_a?(::Module)
|
45
|
-
desc.define_method(:described_class) { const }
|
46
|
-
desc.send(:protected, :described_class)
|
47
|
-
end
|
48
|
-
|
149
|
+
def self.describe(const, &block)
|
150
|
+
desc = ::Class.new(self)
|
151
|
+
desc.let(:described_class) { const } if const.is_a?(::Module)
|
49
152
|
desc.instance_eval(&block)
|
50
|
-
desc
|
51
153
|
end
|
52
154
|
|
53
|
-
#
|
54
|
-
|
155
|
+
# Defines an example group that establishes a specific context, like _empty
|
156
|
+
# array_ versus _array with elements_.
|
157
|
+
#
|
158
|
+
# It is functionally equivalent to {.describe}.
|
159
|
+
#
|
160
|
+
# @example
|
161
|
+
# require "r_spec"
|
162
|
+
#
|
163
|
+
# RSpec.describe "web resource" do
|
164
|
+
# context "when resource is not found" do
|
165
|
+
# pending "responds with 404"
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# context "when resource is found" do
|
169
|
+
# pending "responds with 200"
|
170
|
+
# end
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# # Output to the console
|
174
|
+
# # Warning: responds with 404.
|
175
|
+
# # Warning: responds with 200.
|
176
|
+
#
|
177
|
+
# @param _description [String] A description that usually begins with
|
178
|
+
# "when", "with" or "without".
|
179
|
+
# @param block [Proc] The block to define the specs.
|
180
|
+
def self.context(_description = nil, &block)
|
181
|
+
desc = ::Class.new(self)
|
182
|
+
desc.instance_eval(&block)
|
183
|
+
end
|
55
184
|
|
56
|
-
#
|
57
|
-
# test the state of the code.
|
185
|
+
# Defines a concrete test case.
|
58
186
|
#
|
187
|
+
# The test is performed by the block supplied to `&block`.
|
188
|
+
#
|
189
|
+
# @example The integer after 41
|
190
|
+
# require "r_spec"
|
191
|
+
#
|
192
|
+
# RSpec.describe Integer do
|
193
|
+
# it { expect(41.next).to be 42 }
|
194
|
+
# end
|
195
|
+
#
|
196
|
+
# # Output to the console
|
197
|
+
# # Success: expected to be 42.
|
198
|
+
#
|
199
|
+
# @example A division by zero
|
200
|
+
# require "r_spec"
|
201
|
+
#
|
202
|
+
# RSpec.describe Integer do
|
203
|
+
# subject { 41 }
|
204
|
+
#
|
205
|
+
# it { is_expected.to be_an_instance_of described_class }
|
206
|
+
#
|
207
|
+
# it "raises an error" do
|
208
|
+
# expect { subject / 0 }.to raise_exception ZeroDivisionError
|
209
|
+
# end
|
210
|
+
# end
|
211
|
+
#
|
212
|
+
# # Output to the console
|
213
|
+
# # Success: expected 41 to be an instance of Integer.
|
214
|
+
# # Success: divided by 0.
|
215
|
+
#
|
216
|
+
# It can be used inside a {.describe} or {.context} section.
|
217
|
+
#
|
218
|
+
# @param _name [String, nil] The name of the spec.
|
59
219
|
# @param block [Proc] An expectation to evaluate.
|
60
220
|
#
|
61
221
|
# @raise (see ExpectationTarget::Base#result)
|
62
222
|
# @return (see ExpectationTarget::Base#result)
|
63
223
|
def self.it(_name = nil, &block)
|
64
|
-
raise ::ArgumentError, "Missing block" unless block
|
224
|
+
raise ::ArgumentError, "Missing example block" unless block
|
65
225
|
|
66
|
-
|
226
|
+
example = ::Class.new(self) { include ExpectationHelper::It }.new
|
227
|
+
example.instance_eval(&block)
|
228
|
+
rescue ::SystemExit
|
229
|
+
Console.source(*block.source_location)
|
67
230
|
|
68
|
-
|
69
|
-
|
231
|
+
exit false
|
232
|
+
ensure
|
233
|
+
example&.send(AFTER_METHOD)
|
70
234
|
end
|
71
235
|
|
72
|
-
#
|
73
|
-
#
|
236
|
+
# Use the {.its} method to define a single spec that specifies the actual
|
237
|
+
# value of an attribute of the subject using
|
238
|
+
# {ExpectationHelper::Its#is_expected}.
|
74
239
|
#
|
75
|
-
# @example The
|
240
|
+
# @example The integer after 41
|
76
241
|
# require "r_spec"
|
77
242
|
#
|
78
243
|
# RSpec.describe Integer do
|
@@ -81,55 +246,100 @@ module RSpec
|
|
81
246
|
# its(:next) { is_expected.to be 42 }
|
82
247
|
# end
|
83
248
|
#
|
84
|
-
#
|
249
|
+
# # Output to the console
|
250
|
+
# # Success: expected to be 42.
|
251
|
+
#
|
252
|
+
# @example A division by zero
|
85
253
|
# require "r_spec"
|
86
254
|
#
|
87
255
|
# RSpec.describe Integer do
|
88
|
-
#
|
256
|
+
# subject { 41 }
|
257
|
+
#
|
258
|
+
# its(:/, 0) { is_expected.to raise_exception ZeroDivisionError }
|
89
259
|
# end
|
90
260
|
#
|
261
|
+
# # Output to the console
|
262
|
+
# # Success: divided by 0.
|
263
|
+
#
|
264
|
+
# @example A spec without subject
|
265
|
+
# require "r_spec"
|
266
|
+
#
|
267
|
+
# RSpec.describe Integer do
|
268
|
+
# its(:boom) { is_expected.to raise_exception RSpec::Error::UndefinedSubject }
|
269
|
+
# end
|
270
|
+
#
|
271
|
+
# # Output to the console
|
272
|
+
# # Success: subject not explicitly defined.
|
273
|
+
#
|
91
274
|
# @param attribute [String, Symbol] The property to call to subject.
|
275
|
+
# @param args [Array] An optional list of arguments.
|
276
|
+
# @param kwargs [Hash] An optional list of keyword arguments.
|
277
|
+
# @param block [Proc] An expectation to evaluate.
|
92
278
|
#
|
93
279
|
# @raise (see ExpectationTarget::Base#result)
|
94
280
|
# @return (see ExpectationTarget::Base#result)
|
95
281
|
def self.its(attribute, *args, **kwargs, &block)
|
96
|
-
raise ::ArgumentError, "Missing block" unless block
|
282
|
+
raise ::ArgumentError, "Missing example block" unless block
|
97
283
|
|
98
|
-
|
284
|
+
example = ::Class.new(self) do
|
285
|
+
include ExpectationHelper::Its
|
99
286
|
|
100
|
-
|
287
|
+
define_method(:actual) do
|
288
|
+
subject.public_send(attribute, *args, **kwargs)
|
289
|
+
end
|
290
|
+
end.new
|
101
291
|
|
102
|
-
|
103
|
-
|
104
|
-
|
292
|
+
example.instance_eval(&block)
|
293
|
+
rescue ::SystemExit
|
294
|
+
Console.source(*block.source_location)
|
105
295
|
|
106
|
-
|
296
|
+
exit false
|
297
|
+
ensure
|
298
|
+
example&.send(AFTER_METHOD)
|
107
299
|
end
|
108
300
|
|
109
|
-
#
|
301
|
+
# Defines a pending test case.
|
302
|
+
#
|
303
|
+
# `&block` is never evaluated. It can be used to describe behaviour that is
|
304
|
+
# not yet implemented.
|
305
|
+
#
|
306
|
+
# @example
|
307
|
+
# require "r_spec"
|
308
|
+
#
|
309
|
+
# RSpec.describe "an example" do
|
310
|
+
# pending "is implemented but waiting" do
|
311
|
+
# expect something to be finished
|
312
|
+
# end
|
313
|
+
#
|
314
|
+
# pending "is not yet implemented and waiting"
|
315
|
+
# end
|
316
|
+
#
|
317
|
+
# # Output to the console
|
318
|
+
# # Warning: is implemented but waiting.
|
319
|
+
# # Warning: is not yet implemented and waiting.
|
320
|
+
#
|
321
|
+
# @param message [String] The reason why the example is pending.
|
322
|
+
#
|
323
|
+
# @return [nil] Write a message to STDOUT.
|
110
324
|
#
|
111
|
-
# @
|
112
|
-
def self.
|
113
|
-
::
|
325
|
+
# @api public
|
326
|
+
def self.pending(message)
|
327
|
+
Console.passed_spec Error::PendingExpectation.result(message)
|
114
328
|
end
|
115
329
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
330
|
+
private
|
331
|
+
|
332
|
+
def described_class
|
333
|
+
raise Error::UndefinedDescribedClass,
|
334
|
+
"the first argument to at least one example group must be a module"
|
121
335
|
end
|
122
336
|
|
123
|
-
|
124
|
-
|
125
|
-
# @return [String] A random constant name for a test class.
|
126
|
-
def self.random_test_const_name
|
127
|
-
"Test#{::SecureRandom.hex(4).to_i(16)}"
|
337
|
+
def subject
|
338
|
+
raise Error::UndefinedSubject, "subject not explicitly defined"
|
128
339
|
end
|
129
340
|
|
130
|
-
|
341
|
+
define_method(AFTER_METHOD) do
|
342
|
+
# do nothing by default
|
343
|
+
end
|
131
344
|
end
|
132
345
|
end
|
133
|
-
|
134
|
-
require_relative "expectation_helper"
|
135
|
-
require_relative "sandbox"
|
data/lib/r_spec/error.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative File.join("error", "pending_expectation")
|
4
|
+
require_relative File.join("error", "reserved_method")
|
5
|
+
require_relative File.join("error", "undefined_described_class")
|
6
|
+
require_relative File.join("error", "undefined_subject")
|
7
|
+
|
8
|
+
module RSpec
|
9
|
+
# Namespace for exceptions.
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
module Error
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "expresenter"
|
4
|
+
|
5
|
+
module RSpec
|
6
|
+
module Error
|
7
|
+
# Exception for pending expectations.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class PendingExpectation < ::RuntimeError
|
11
|
+
# @param message [String] The not implemented expectation description.
|
12
|
+
#
|
13
|
+
# @return [nil] Write a pending expectation to STDOUT.
|
14
|
+
def self.result(message)
|
15
|
+
::Expresenter.call(true).with(
|
16
|
+
actual: new(message),
|
17
|
+
error: nil,
|
18
|
+
expected: self,
|
19
|
+
got: false,
|
20
|
+
matcher: :raise_exception,
|
21
|
+
negate: true,
|
22
|
+
level: :SHOULD
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative File.join("expectation_helper", "it")
|
4
|
+
require_relative File.join("expectation_helper", "its")
|
5
|
+
|
3
6
|
module RSpec
|
4
|
-
# Namespace for
|
7
|
+
# Namespace for {Dsl.it} and {Dsl.its}'s helper modules.
|
5
8
|
module ExpectationHelper
|
6
9
|
end
|
7
10
|
end
|
8
|
-
|
9
|
-
require_relative File.join("expectation_helper", "it")
|
10
|
-
require_relative File.join("expectation_helper", "its")
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "shared"
|
4
4
|
require_relative File.join("..", "expectation_target")
|
5
5
|
|
6
6
|
module RSpec
|
7
7
|
module ExpectationHelper
|
8
|
-
#
|
8
|
+
# {RSpec::Dsl.it}'s expectation helper module.
|
9
9
|
module It
|
10
|
-
include
|
10
|
+
include Shared
|
11
11
|
|
12
12
|
# Create an expectation for a spec.
|
13
13
|
#
|
@@ -27,7 +27,7 @@ module RSpec
|
|
27
27
|
|
28
28
|
# Wraps the target of an expectation with the subject as actual value.
|
29
29
|
#
|
30
|
-
# @return
|
30
|
+
# @return [Block] The wrapped target of an expectation.
|
31
31
|
#
|
32
32
|
# @example
|
33
33
|
# is_expected # => #<RSpec::ExpectationTarget::Block:0x00007fb6b8263df8 @callable=#<Proc:0x00007fb6b8263e20>>
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "shared"
|
4
4
|
require_relative File.join("..", "expectation_target", "block")
|
5
5
|
|
6
6
|
module RSpec
|
7
7
|
module ExpectationHelper
|
8
|
-
#
|
8
|
+
# {RSpec::Dsl.its}'s expectation helper module.
|
9
9
|
module Its
|
10
|
-
include
|
10
|
+
include Shared
|
11
11
|
|
12
12
|
# Wraps the target of an expectation with the actual value.
|
13
13
|
#
|
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
require "matchi/rspec"
|
4
4
|
|
5
|
-
require_relative File.join("..", "
|
5
|
+
require_relative File.join("..", "error", "pending_expectation")
|
6
6
|
|
7
7
|
module RSpec
|
8
8
|
module ExpectationHelper
|
9
9
|
# Abstract expectation helper base module.
|
10
10
|
#
|
11
11
|
# This module defines a number of methods to create expectations, which are
|
12
|
-
# automatically included into
|
12
|
+
# automatically included into examples.
|
13
13
|
#
|
14
14
|
# It also includes a collection of expectation matchers 🤹
|
15
15
|
#
|
@@ -75,22 +75,8 @@ module RSpec
|
|
75
75
|
#
|
76
76
|
# @see https://github.com/fixrb/matchi
|
77
77
|
# @see https://github.com/fixrb/matchi-rspec
|
78
|
-
module
|
78
|
+
module Shared
|
79
79
|
include ::Matchi::Helper
|
80
|
-
|
81
|
-
# Mark a spec as pending, expectation results will be ignored.
|
82
|
-
#
|
83
|
-
# @param description [String] The reason why the example is pending.
|
84
|
-
#
|
85
|
-
# @return [nil] Write a message to STDOUT.
|
86
|
-
#
|
87
|
-
# @example Output a message to the console and return nil
|
88
|
-
# pending("something else getting finished") # => nil
|
89
|
-
#
|
90
|
-
# @api public
|
91
|
-
def pending(description)
|
92
|
-
Pending.result(description)
|
93
|
-
end
|
94
80
|
end
|
95
81
|
end
|
96
82
|
end
|
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative File.join("expectation_target", "block")
|
4
|
+
require_relative File.join("expectation_target", "value")
|
5
|
+
|
3
6
|
module RSpec
|
4
7
|
# Wraps the target of an expectation.
|
5
8
|
#
|
@@ -25,6 +28,3 @@ module RSpec
|
|
25
28
|
end
|
26
29
|
end
|
27
30
|
end
|
28
|
-
|
29
|
-
require_relative File.join("expectation_target", "block")
|
30
|
-
require_relative File.join("expectation_target", "value")
|
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "expresenter"
|
4
|
+
require "test_tube"
|
5
|
+
|
6
|
+
require_relative File.join("..", "console")
|
4
7
|
|
5
8
|
module RSpec
|
6
9
|
module ExpectationTarget
|
@@ -9,6 +12,13 @@ module RSpec
|
|
9
12
|
# @note `RSpec::ExpectationTarget::Base` is not intended to be instantiated
|
10
13
|
# directly by users. Use `expect` instead.
|
11
14
|
class Base
|
15
|
+
# Instantiate a new expectation target.
|
16
|
+
#
|
17
|
+
# @param actual [#object_id] The actual value of the code to evaluate.
|
18
|
+
def initialize(actual)
|
19
|
+
@actual = actual
|
20
|
+
end
|
21
|
+
|
12
22
|
# Runs the given expectation, passing if `matcher` returns true.
|
13
23
|
#
|
14
24
|
# @example _Absolute requirement_ definition
|
@@ -41,12 +51,12 @@ module RSpec
|
|
41
51
|
|
42
52
|
protected
|
43
53
|
|
54
|
+
# @param passed [Boolean] The high expectation passed or failed.
|
44
55
|
# @param actual [#object_id] The actual value.
|
45
56
|
# @param error [Exception, nil] Any raised exception.
|
46
57
|
# @param got [Boolean, nil] Any returned value.
|
47
58
|
# @param matcher [#matches?] The matcher.
|
48
59
|
# @param negate [Boolean] The assertion is positive or negative.
|
49
|
-
# @param valid [Boolean] The result of an expectation.
|
50
60
|
#
|
51
61
|
# @return [nil] Write a message to STDOUT.
|
52
62
|
#
|
@@ -54,19 +64,18 @@ module RSpec
|
|
54
64
|
# `Kernel.exit(false)` with a failure message written to STDERR.
|
55
65
|
#
|
56
66
|
# @api private
|
57
|
-
def result(actual:, error:, got:, matcher:, negate
|
58
|
-
|
67
|
+
def result(passed, actual:, error:, got:, matcher:, negate:)
|
68
|
+
Console.passed_spec ::Expresenter.call(passed).with(
|
59
69
|
actual: actual,
|
60
70
|
error: error,
|
61
71
|
expected: matcher.expected,
|
62
72
|
got: got,
|
63
73
|
negate: negate,
|
64
|
-
valid: valid,
|
65
74
|
matcher: matcher.class.to_sym,
|
66
75
|
level: :MUST
|
67
|
-
)
|
76
|
+
)
|
68
77
|
rescue ::Expresenter::Fail => e
|
69
|
-
|
78
|
+
Console.failed_spec(e)
|
70
79
|
end
|
71
80
|
end
|
72
81
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "spectus/exam"
|
4
|
-
|
5
3
|
require_relative "base"
|
6
4
|
|
7
5
|
module RSpec
|
@@ -20,15 +18,6 @@ module RSpec
|
|
20
18
|
# @note `RSpec::ExpectationTarget::Block` is not intended to be instantiated
|
21
19
|
# directly by users. Use `expect` instead.
|
22
20
|
class Block < Base
|
23
|
-
# Instantiate a new expectation target.
|
24
|
-
#
|
25
|
-
# @param block [#call] The code to evaluate.
|
26
|
-
def initialize(block)
|
27
|
-
super()
|
28
|
-
|
29
|
-
@callable = block
|
30
|
-
end
|
31
|
-
|
32
21
|
protected
|
33
22
|
|
34
23
|
# @param matcher [#matches?] The matcher.
|
@@ -39,20 +28,20 @@ module RSpec
|
|
39
28
|
# @raise [SystemExit] Terminate execution immediately by calling
|
40
29
|
# `Kernel.exit(false)` with a failure message written to STDERR.
|
41
30
|
def absolute_requirement(matcher:, negate:)
|
42
|
-
|
43
|
-
|
31
|
+
experiment = ::TestTube.invoke(
|
32
|
+
@actual,
|
44
33
|
isolation: false,
|
45
|
-
|
46
|
-
|
34
|
+
matcher: matcher,
|
35
|
+
negate: negate
|
47
36
|
)
|
48
37
|
|
49
38
|
result(
|
50
|
-
|
51
|
-
|
52
|
-
|
39
|
+
experiment.got.equal?(true),
|
40
|
+
actual: experiment.actual,
|
41
|
+
error: experiment.error,
|
42
|
+
got: experiment.got,
|
53
43
|
matcher: matcher,
|
54
|
-
negate: negate
|
55
|
-
valid: exam.valid?
|
44
|
+
negate: negate
|
56
45
|
)
|
57
46
|
end
|
58
47
|
end
|
@@ -18,15 +18,6 @@ module RSpec
|
|
18
18
|
# @note `RSpec::ExpectationTarget::Value` is not intended to be instantiated
|
19
19
|
# directly by users. Use `expect` instead.
|
20
20
|
class Value < Base
|
21
|
-
# Instantiate a new expectation target.
|
22
|
-
#
|
23
|
-
# @param actual [#object_id] The actual value.
|
24
|
-
def initialize(actual)
|
25
|
-
super()
|
26
|
-
|
27
|
-
@actual = actual
|
28
|
-
end
|
29
|
-
|
30
21
|
protected
|
31
22
|
|
32
23
|
# @param matcher [#matches?] The matcher.
|
@@ -37,15 +28,19 @@ module RSpec
|
|
37
28
|
# @raise [SystemExit] Terminate execution immediately by calling
|
38
29
|
# `Kernel.exit(false)` with a failure message written to STDERR.
|
39
30
|
def absolute_requirement(matcher:, negate:)
|
40
|
-
|
31
|
+
experiment = ::TestTube.pass(
|
32
|
+
@actual,
|
33
|
+
matcher: matcher,
|
34
|
+
negate: negate
|
35
|
+
)
|
41
36
|
|
42
37
|
result(
|
43
|
-
|
44
|
-
|
45
|
-
|
38
|
+
experiment.got.equal?(true),
|
39
|
+
actual: experiment.actual,
|
40
|
+
error: experiment.error,
|
41
|
+
got: experiment.got,
|
46
42
|
matcher: matcher,
|
47
|
-
negate: negate
|
48
|
-
valid: valid
|
43
|
+
negate: negate
|
49
44
|
)
|
50
45
|
end
|
51
46
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: r_spec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta9
|
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-06-
|
11
|
+
date: 2021-06-19 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.
|
19
|
+
version: 1.3.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.
|
26
|
+
version: 1.3.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: matchi-rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 1.1.
|
33
|
+
version: 1.1.2
|
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.1.
|
40
|
+
version: 1.1.2
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
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: 1.0.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: 1.0.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -187,17 +187,21 @@ files:
|
|
187
187
|
- LICENSE.md
|
188
188
|
- README.md
|
189
189
|
- lib/r_spec.rb
|
190
|
+
- lib/r_spec/console.rb
|
190
191
|
- lib/r_spec/dsl.rb
|
192
|
+
- lib/r_spec/error.rb
|
193
|
+
- lib/r_spec/error/pending_expectation.rb
|
194
|
+
- lib/r_spec/error/reserved_method.rb
|
195
|
+
- lib/r_spec/error/undefined_described_class.rb
|
196
|
+
- lib/r_spec/error/undefined_subject.rb
|
191
197
|
- lib/r_spec/expectation_helper.rb
|
192
|
-
- lib/r_spec/expectation_helper/base.rb
|
193
198
|
- lib/r_spec/expectation_helper/it.rb
|
194
199
|
- lib/r_spec/expectation_helper/its.rb
|
200
|
+
- lib/r_spec/expectation_helper/shared.rb
|
195
201
|
- lib/r_spec/expectation_target.rb
|
196
202
|
- lib/r_spec/expectation_target/base.rb
|
197
203
|
- lib/r_spec/expectation_target/block.rb
|
198
204
|
- lib/r_spec/expectation_target/value.rb
|
199
|
-
- lib/r_spec/pending.rb
|
200
|
-
- lib/r_spec/sandbox.rb
|
201
205
|
homepage: https://r-spec.dev/
|
202
206
|
licenses:
|
203
207
|
- MIT
|
data/lib/r_spec/pending.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "expresenter"
|
4
|
-
|
5
|
-
module RSpec
|
6
|
-
# Exception for pending expectations.
|
7
|
-
#
|
8
|
-
# @api private
|
9
|
-
class Pending < ::NotImplementedError
|
10
|
-
# @param message [String] The not implemented expectation description.
|
11
|
-
#
|
12
|
-
# @return [nil] Write a pending expectation to STDOUT.
|
13
|
-
def self.result(message)
|
14
|
-
warn " " + ::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,
|
22
|
-
valid: false
|
23
|
-
).colored_string
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|