r_spec 1.0.0.beta3 → 1.0.0.beta4
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 +101 -160
- data/lib/r_spec.rb +44 -4
- data/lib/r_spec/dsl.rb +69 -58
- data/lib/r_spec/expectation_helper.rb +10 -0
- data/lib/r_spec/expectation_helper/base.rb +96 -0
- data/lib/r_spec/expectation_helper/it.rb +41 -0
- data/lib/r_spec/expectation_helper/its.rb +25 -0
- data/lib/r_spec/expectation_target.rb +4 -2
- data/lib/r_spec/expectation_target/base.rb +9 -2
- data/lib/r_spec/expectation_target/block.rb +6 -8
- data/lib/r_spec/expectation_target/value.rb +6 -8
- data/lib/r_spec/pending.rb +2 -0
- data/lib/r_spec/{test.rb → sandbox.rb} +3 -1
- metadata +10 -6
- data/lib/r_spec/log.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d94ad6d7ba01dc04c4bcbe727543524f6a3d83cf69549a34453c1eefc5282e9
|
4
|
+
data.tar.gz: 61b3ebe1299e69d7ef1f2c180325e85e1bda05e2347176ae507fc7430cf311ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d44ce99f58c17711be4b3f76cdfaf3c9923d031aa9a7d214db50b990825673b9bb485ce095195594fbeb45977763dc5a0be31770f089cd17dcb1e3ef1d6885b
|
7
|
+
data.tar.gz: ca64a27f7cf4032b6b073b9fc2eb05aa37654c02a58289c41094e9afa000deccc3886d03d2043642a02e38cf10b6cc0fbd775e7ef9bc1c29cb592b813dcf5d04
|
data/README.md
CHANGED
@@ -1,27 +1,32 @@
|
|
1
1
|
# RSpec clone
|
2
2
|
|
3
|
-
A minimalist [RSpec](https://github.com/rspec/rspec)
|
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
11
|
[](https://inch-ci.org/github/cyril/r_spec.rb)
|
12
|
+
[](https://rubydoc.info/gems/r_spec/frames)
|
12
13
|
|
13
14
|
## Goal
|
14
15
|
|
15
|
-
This clone attempts to provide most of RSpec's DSL without magic power.
|
16
|
+
This clone attempts to provide most of RSpec's DSL to express expected outcomes of a code example without magic power.
|
16
17
|
|
17
18
|
## Some differences
|
18
19
|
|
19
20
|
* Less features and an implementation with much less code complexity.
|
20
21
|
* Spec files can also be executed directly with the `ruby` executable.
|
21
22
|
* There is no option to activate monkey-patching.
|
22
|
-
*
|
23
|
-
* Built-in matchers do not trust _actual_ and do not send it
|
24
|
-
* The subject must be explicitly defined, otherwise it is not implemented.
|
23
|
+
* It does not rely on hacks such as `at_exit` hook to trigger the tests.
|
24
|
+
* Built-in matchers do not trust _actual_ and do not send it messages.
|
25
|
+
* The `subject` must be explicitly defined, otherwise it is not implemented.
|
26
|
+
* Expectations cannot be added inside a `before` block.
|
27
|
+
* [Arbitrary helper methods](https://relishapp.com/rspec/rspec-core/v/3-10/docs/helper-methods/arbitrary-helper-methods) are not exposed to examples.
|
28
|
+
* The `let` method defines a helper method rather than a memoized helper method.
|
29
|
+
* The one-liner `is_expected` syntax also works with block expectations.
|
25
30
|
|
26
31
|
## Important ⚠️
|
27
32
|
|
@@ -40,7 +45,7 @@ Following [RubyGems naming conventions](https://guides.rubygems.org/name-your-ge
|
|
40
45
|
Add this line to your application's Gemfile:
|
41
46
|
|
42
47
|
```ruby
|
43
|
-
gem "r_spec", ">= 1.0.0.
|
48
|
+
gem "r_spec", ">= 1.0.0.beta4"
|
44
49
|
```
|
45
50
|
|
46
51
|
And then execute:
|
@@ -55,223 +60,159 @@ Or install it yourself as:
|
|
55
60
|
gem install r_spec --pre
|
56
61
|
```
|
57
62
|
|
63
|
+
## Overview
|
64
|
+
|
65
|
+
__RSpec clone__ provides a structure for writing executable examples of how your code should behave.
|
66
|
+
|
67
|
+
Inspired by [RSpec](https://rspec.info/), it includes a domain specific language (DSL) that allows you to write examples in a way similar to plain english.
|
68
|
+
|
69
|
+
A basic spec looks something like this:
|
70
|
+
|
71
|
+
[](https://asciinema.org/a/418672?autoplay=1)
|
72
|
+
|
58
73
|
## Usage
|
59
74
|
|
60
|
-
|
75
|
+
### Anatomy of a spec file
|
61
76
|
|
62
|
-
|
77
|
+
To use the `RSpec` module and its DSL, you need to add `require "r_spec"` to your spec files.
|
78
|
+
Many projects use a custom spec helper which organizes these includes.
|
63
79
|
|
64
|
-
|
80
|
+
Concrete test cases are defined in `it` blocks.
|
81
|
+
An optional descriptive string states it's purpose and a block contains the main logic performing the test.
|
65
82
|
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
```
|
83
|
+
An `it` block contains an example that should invoke the code to be tested and define what is expected of it.
|
84
|
+
Each example can contain multiple expectations, but it should test only one specific behaviour.
|
70
85
|
|
71
|
-
|
86
|
+
To express an expectation, wrap an object or block in `expect`, call `to` or `not_to` and pass it a matcher object.
|
72
87
|
|
73
|
-
|
74
|
-
|
75
|
-
class Test3582143298
|
76
|
-
protected
|
77
|
-
|
78
|
-
def described_class
|
79
|
-
String
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
```
|
88
|
+
If the expectation is met, code execution continues.
|
89
|
+
Otherwise the example has _failed_ and other code will not be executed.
|
84
90
|
|
85
|
-
|
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.
|
86
92
|
|
87
|
-
|
93
|
+
In test files, specs are structured by example groups which are defined by `describe` and `context` sections.
|
94
|
+
Typically a top level `describe` defines the outer unit (such as a class) to be tested by the spec.
|
95
|
+
Further `describe` sections can be nested within the outer unit to specify smaller units under test (such as individual methods).
|
88
96
|
|
89
|
-
|
97
|
+
For unit tests, it is recommended to follow the conventions for method names:
|
90
98
|
|
91
|
-
|
99
|
+
* outer `describe` is the name of the class, inner `describe` targets methods;
|
100
|
+
* instance methods are prefixed with `#`, class methods with `.`.
|
92
101
|
|
93
|
-
|
94
|
-
|
95
|
-
subject do
|
96
|
-
:foo
|
97
|
-
end
|
98
|
-
end
|
99
|
-
```
|
102
|
+
To establish certain contexts - think _empty array_ versus _array with elements_ - the `context` method may be used to communicate this to the reader.
|
103
|
+
It has a different name, but behaves exactly like `describe`.
|
100
104
|
|
101
|
-
|
105
|
+
`describe` and `context` take an optional description as argument and a block containing the individual specs or nested groupings.
|
102
106
|
|
103
|
-
|
104
|
-
module RSpec::Test
|
105
|
-
class Test3582143298
|
106
|
-
protected
|
107
|
-
|
108
|
-
def subject
|
109
|
-
:foo
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
```
|
107
|
+
### Expectations
|
114
108
|
|
115
|
-
|
109
|
+
Expectations define if the value being tested (_actual_) matches a certain value or specific criteria.
|
116
110
|
|
117
|
-
|
111
|
+
#### Equivalence
|
118
112
|
|
119
113
|
```ruby
|
120
|
-
|
121
|
-
#
|
122
|
-
|
123
|
-
describe "Embedded describe" do
|
124
|
-
# embedded describe block
|
125
|
-
end
|
126
|
-
end
|
114
|
+
expect(actual).to eql(expected) # passes if expected.eql?(actual)
|
115
|
+
expect(actual).to eq(expected) # passes if expected.eql?(actual)
|
127
116
|
```
|
128
117
|
|
129
|
-
|
118
|
+
#### Identity
|
130
119
|
|
131
120
|
```ruby
|
132
|
-
|
133
|
-
|
134
|
-
# main describe block
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
module RSpec::Test
|
139
|
-
class Test198623541 < RSpec::Test::Test3582143298
|
140
|
-
# embedded describe block
|
141
|
-
end
|
142
|
-
end
|
121
|
+
expect(actual).to equal(expected) # passes if expected.equal?(actual)
|
122
|
+
expect(actual).to be(expected) # passes if expected.equal?(actual)
|
143
123
|
```
|
144
124
|
|
145
|
-
|
146
|
-
|
147
|
-
Example of specification content:
|
125
|
+
#### Regular expressions
|
148
126
|
|
149
127
|
```ruby
|
150
|
-
|
151
|
-
let(:var0) { 42 }
|
152
|
-
let(:var1) { 42 + var3 }
|
153
|
-
let(:var3) { 42 }
|
154
|
-
end
|
128
|
+
expect(actual).to match(expected) # passes if expected.match?(actual)
|
155
129
|
```
|
156
130
|
|
157
|
-
|
131
|
+
#### Expecting errors
|
158
132
|
|
159
133
|
```ruby
|
160
|
-
|
161
|
-
class Test3582143298
|
162
|
-
protected
|
163
|
-
|
164
|
-
def var0
|
165
|
-
42
|
166
|
-
end
|
167
|
-
|
168
|
-
def var1
|
169
|
-
42 + var3
|
170
|
-
end
|
171
|
-
|
172
|
-
def var3
|
173
|
-
42
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
134
|
+
expect { actual }.to raise_exception(expected) # passes if expected exception is raised
|
177
135
|
```
|
178
136
|
|
179
|
-
|
180
|
-
|
181
|
-
Example of specification content:
|
137
|
+
#### Truth
|
182
138
|
|
183
139
|
```ruby
|
184
|
-
|
185
|
-
before do
|
186
|
-
puts "hello"
|
187
|
-
end
|
188
|
-
end
|
140
|
+
expect(actual).to be_true # passes if true.equal?(actual)
|
189
141
|
```
|
190
142
|
|
191
|
-
|
143
|
+
#### Untruth
|
192
144
|
|
193
145
|
```ruby
|
194
|
-
|
195
|
-
class Test3582143298
|
196
|
-
def initialize
|
197
|
-
puts "hello"
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
146
|
+
expect(actual).to be_false # passes if false.equal?(actual)
|
201
147
|
```
|
202
148
|
|
203
|
-
|
204
|
-
|
205
|
-
Example of specification content:
|
149
|
+
#### Nil
|
206
150
|
|
207
151
|
```ruby
|
208
|
-
|
209
|
-
it { expect(41.next).to be(42) }
|
210
|
-
end
|
152
|
+
expect(actual).to be_nil # passes if nil.equal?(actual)
|
211
153
|
```
|
212
154
|
|
213
|
-
|
155
|
+
#### Type/class
|
214
156
|
|
215
157
|
```ruby
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
end
|
158
|
+
expect(actual).to be_instance_of(expected) # passes if expected.equal?(actual.class)
|
159
|
+
expect(actual).to be_an_instance_of(expected) # passes if expected.equal?(actual.class)
|
160
|
+
```
|
220
161
|
|
221
|
-
|
222
|
-
include Matchi::Helper
|
162
|
+
### Running specs
|
223
163
|
|
224
|
-
|
225
|
-
end
|
164
|
+
By convention, specs live in the `spec/` directory of a project. Spec files should end with `_spec.rb` to be recognizable as such.
|
226
165
|
|
227
|
-
|
228
|
-
|
229
|
-
|
166
|
+
Depending of the project settings, you may run the specs of a project by running `rake spec` (see [`rake` integration example](#rake-integration-example) below).
|
167
|
+
A single file can also be executed directly with the Ruby interpreter.
|
168
|
+
|
169
|
+
#### Examples
|
170
|
+
|
171
|
+
Run all specs in files matching `spec/**/*_spec.rb`:
|
172
|
+
|
173
|
+
```sh
|
174
|
+
bundle exec rake spec
|
230
175
|
```
|
231
176
|
|
232
|
-
|
177
|
+
Run a single file:
|
233
178
|
|
234
|
-
|
179
|
+
```sh
|
180
|
+
ruby spec/my/test/file_spec.rb
|
181
|
+
```
|
235
182
|
|
236
|
-
|
183
|
+
I know that sounds weird, but the [`rspec` command line](https://relishapp.com/rspec/rspec-core/docs/command-line) is also working pretty well:
|
237
184
|
|
238
|
-
```
|
239
|
-
|
185
|
+
```sh
|
186
|
+
rspec spec/my/test/file_spec.rb
|
187
|
+
rspec spec/my/test/file_spec.rb:42
|
188
|
+
rspec spec/my/test/
|
189
|
+
rspec
|
190
|
+
```
|
240
191
|
|
241
|
-
|
192
|
+
### Spec helper
|
242
193
|
|
243
|
-
|
244
|
-
before do
|
245
|
-
@elements = described_class.new
|
246
|
-
end
|
194
|
+
Many projects use a custom spec helper file, usually named `spec/spec_helper.rb`.
|
247
195
|
|
248
|
-
|
249
|
-
subject do
|
250
|
-
@elements.count
|
251
|
-
end
|
196
|
+
This file is used to require `r_spec` and other includes, like the code from the project needed for every spec file.
|
252
197
|
|
253
|
-
|
198
|
+
### `rake` integration example
|
254
199
|
|
255
|
-
|
256
|
-
before do
|
257
|
-
@elements << 1
|
258
|
-
end
|
200
|
+
The following `Rakefile` settings should be enough:
|
259
201
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
end
|
264
|
-
```
|
202
|
+
```ruby
|
203
|
+
require "bundler/gem_tasks"
|
204
|
+
require "rake/testtask"
|
265
205
|
|
266
|
-
|
206
|
+
Rake::TestTask.new do |t|
|
207
|
+
t.pattern = "spec/**/*_spec.rb"
|
208
|
+
t.verbose = true
|
209
|
+
t.warning = true
|
210
|
+
end
|
267
211
|
|
268
|
-
|
269
|
-
|
212
|
+
task spec: :test
|
213
|
+
task default: :test
|
270
214
|
```
|
271
215
|
|
272
|
-
array_spec.rb:15 Success: expected to be 0.
|
273
|
-
array_spec.rb:22 Success: expected to be 1.
|
274
|
-
|
275
216
|
## Test suite
|
276
217
|
|
277
218
|
__RSpec clone__'s specifications are self-described here: [spec/](https://github.com/cyril/r_spec.rb/blob/main/spec/)
|
data/lib/r_spec.rb
CHANGED
@@ -2,11 +2,51 @@
|
|
2
2
|
|
3
3
|
# Top level namespace for the RSpec clone.
|
4
4
|
#
|
5
|
-
# @example
|
5
|
+
# @example The true from the false
|
6
|
+
# require "r_spec"
|
7
|
+
#
|
8
|
+
# RSpec.describe do
|
9
|
+
# it { expect(false).not_to be true }
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# @example The basic behavior of arrays
|
13
|
+
# require "r_spec"
|
14
|
+
#
|
15
|
+
# RSpec.describe Array do
|
16
|
+
# describe "#size" do
|
17
|
+
# it "correctly reports the number of elements in the Array" do
|
18
|
+
# expect([1, 2, 3].size).to eq 3
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# describe "#empty?" do
|
23
|
+
# it "is empty when no elements are in the array" do
|
24
|
+
# expect([].empty?).to be_true
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# it "is not empty if there are elements in the array" do
|
28
|
+
# expect([1].empty?).to be_false
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# @example An inherited definition of let
|
6
34
|
# require "r_spec"
|
7
35
|
#
|
8
36
|
# RSpec.describe Integer do
|
9
|
-
#
|
37
|
+
# let(:answer) { 42 }
|
38
|
+
#
|
39
|
+
# it "returns the value" do
|
40
|
+
# expect(answer).to be(42)
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# context "when the number is incremented" do
|
44
|
+
# let(:answer) { super().next }
|
45
|
+
#
|
46
|
+
# it "returns the next value" do
|
47
|
+
# expect(answer).to be(43)
|
48
|
+
# end
|
49
|
+
# end
|
10
50
|
# end
|
11
51
|
#
|
12
52
|
# @api public
|
@@ -17,8 +57,8 @@ module RSpec
|
|
17
57
|
# @param block [Proc] The block to define the specs.
|
18
58
|
#
|
19
59
|
# @api public
|
20
|
-
def self.describe(const, &block)
|
21
|
-
|
60
|
+
def self.describe(const = nil, &block)
|
61
|
+
Dsl.describe(const, &block)
|
22
62
|
end
|
23
63
|
end
|
24
64
|
|
data/lib/r_spec/dsl.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "matchi/rspec"
|
4
3
|
require "securerandom"
|
5
4
|
|
6
5
|
module RSpec
|
7
6
|
# Abstract class for handling the domain-specific language.
|
8
|
-
class
|
7
|
+
class Dsl
|
8
|
+
# Instructs the spec runner to execute the given block before each spec in
|
9
|
+
# the spec suite.
|
10
|
+
#
|
9
11
|
# @param block [Proc] The content to execute at the class initialization.
|
10
12
|
def self.before(&block)
|
11
13
|
define_method(:initialize) do |*args, **kwargs|
|
@@ -14,24 +16,30 @@ module RSpec
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
17
|
-
#
|
19
|
+
# Sets a user-defined property.
|
20
|
+
#
|
21
|
+
# @param name [String, Symbol] The name of the property.
|
22
|
+
# @param block [Proc] The content of the method to define.
|
23
|
+
#
|
18
24
|
# @return [Symbol] A protected method that define the block content.
|
19
|
-
def self.let(name, &block)
|
20
|
-
protected define_method(name.to_sym, &block)
|
25
|
+
def self.let(name, *args, **kwargs, &block)
|
26
|
+
protected define_method(name.to_sym, *args, **kwargs, &block)
|
21
27
|
end
|
22
28
|
|
29
|
+
# Sets a user-defined property named `subject`.
|
30
|
+
#
|
23
31
|
# @param block [Proc] The subject to set.
|
24
32
|
# @return [Symbol] A `subject` method that define the block content.
|
25
33
|
def self.subject(&block)
|
26
34
|
let(__method__, &block)
|
27
35
|
end
|
28
36
|
|
29
|
-
#
|
37
|
+
# Create a group of specs.
|
30
38
|
#
|
31
39
|
# @param const [Module, #object_id] A module to include in block context.
|
32
40
|
# @param block [Proc] The block to define the specs.
|
33
|
-
def self.describe(const, &block)
|
34
|
-
desc =
|
41
|
+
def self.describe(const = nil, &block)
|
42
|
+
desc = Sandbox.const_set(random_test_const_name, ::Class.new(self))
|
35
43
|
|
36
44
|
if const.is_a?(::Module)
|
37
45
|
desc.define_method(:described_class) { const }
|
@@ -45,7 +53,8 @@ module RSpec
|
|
45
53
|
# Add `context` to the DSL.
|
46
54
|
singleton_class.send(:alias_method, :context, :describe)
|
47
55
|
|
48
|
-
#
|
56
|
+
# Define a single spec. A spec should contain one or more expectations that
|
57
|
+
# test the state of the code.
|
49
58
|
#
|
50
59
|
# @param block [Proc] An expectation to evaluate.
|
51
60
|
#
|
@@ -56,55 +65,59 @@ module RSpec
|
|
56
65
|
|
57
66
|
puts "\e[37m#{block.source_location.join(':')}\e[0m"
|
58
67
|
|
59
|
-
i =
|
68
|
+
i = it_example.new
|
60
69
|
i.instance_eval(&block)
|
61
70
|
end
|
62
71
|
|
63
|
-
#
|
72
|
+
# Define a single spec. A spec should contain one or more expectations that
|
73
|
+
# test the state of the code.
|
74
|
+
#
|
75
|
+
# @example The value after 41
|
76
|
+
# require "r_spec"
|
77
|
+
#
|
78
|
+
# RSpec.describe Integer do
|
79
|
+
# subject { 41 }
|
64
80
|
#
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
# Output a message to the console.
|
91
|
-
#
|
92
|
-
# @param message [String] The string to be notified about.
|
93
|
-
#
|
94
|
-
# @return [nil] Write a message to STDOUT.
|
95
|
-
def log(message)
|
96
|
-
Log.result(message)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Indicate that an example is disabled pending some action.
|
100
|
-
#
|
101
|
-
# @param description [String] The reason why the example is pending.
|
102
|
-
#
|
103
|
-
# @return [nil] Write a message to STDOUT.
|
104
|
-
def pending(description)
|
105
|
-
Pending.result(description)
|
106
|
-
end
|
81
|
+
# its(:next) { is_expected.to be 42 }
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# @example Without defining a subject
|
85
|
+
# require "r_spec"
|
86
|
+
#
|
87
|
+
# RSpec.describe Integer do
|
88
|
+
# its(:next) { is_expected.to raise_exception NameError }
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# @param attribute [String, Symbol] The property to call to subject.
|
92
|
+
#
|
93
|
+
# @raise (see ExpectationTarget::Base#result)
|
94
|
+
# @return (see ExpectationTarget::Base#result)
|
95
|
+
def self.its(attribute, *args, **kwargs, &block)
|
96
|
+
raise ::ArgumentError, "Missing block" unless block
|
97
|
+
|
98
|
+
puts "\e[37m#{block.source_location.join(':')}\e[0m"
|
99
|
+
|
100
|
+
i = its_example.new
|
101
|
+
|
102
|
+
i.define_singleton_method(:actual) do
|
103
|
+
subject.public_send(attribute, *args, **kwargs)
|
107
104
|
end
|
105
|
+
|
106
|
+
i.instance_eval(&block)
|
107
|
+
end
|
108
|
+
|
109
|
+
# @private
|
110
|
+
#
|
111
|
+
# @return [Class<Dsl>] The class of the example to be tested.
|
112
|
+
def self.it_example
|
113
|
+
::Class.new(self) { include ExpectationHelper::It }
|
114
|
+
end
|
115
|
+
|
116
|
+
# @private
|
117
|
+
#
|
118
|
+
# @return [Class<Dsl>] The class of the example to be tested.
|
119
|
+
def self.its_example
|
120
|
+
::Class.new(self) { include ExpectationHelper::Its }
|
108
121
|
end
|
109
122
|
|
110
123
|
# @private
|
@@ -114,11 +127,9 @@ module RSpec
|
|
114
127
|
"Test#{::SecureRandom.hex(4).to_i(16)}"
|
115
128
|
end
|
116
129
|
|
117
|
-
private_class_method :
|
130
|
+
private_class_method :it_example, :its_example, :random_test_const_name
|
118
131
|
end
|
119
132
|
end
|
120
133
|
|
121
|
-
require_relative "
|
122
|
-
require_relative "
|
123
|
-
require_relative "pending"
|
124
|
-
require_relative "test"
|
134
|
+
require_relative "expectation_helper"
|
135
|
+
require_relative "sandbox"
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "matchi/rspec"
|
4
|
+
|
5
|
+
require_relative File.join("..", "pending")
|
6
|
+
|
7
|
+
module RSpec
|
8
|
+
module ExpectationHelper
|
9
|
+
# Abstract expectation helper base module.
|
10
|
+
#
|
11
|
+
# This module defines a number of methods to create expectations, which are
|
12
|
+
# automatically included into example namespaces.
|
13
|
+
#
|
14
|
+
# It also includes a collection of expectation matchers 🤹
|
15
|
+
#
|
16
|
+
# @example Equivalence matcher
|
17
|
+
# matcher = eql("foo") # => Matchi::Matcher::Eql.new("foo")
|
18
|
+
# matcher.matches? { "foo" } # => true
|
19
|
+
# matcher.matches? { "bar" } # => false
|
20
|
+
#
|
21
|
+
# matcher = eq("foo") # => Matchi::Matcher::Eq.new("foo")
|
22
|
+
# matcher.matches? { "foo" } # => true
|
23
|
+
# matcher.matches? { "bar" } # => false
|
24
|
+
#
|
25
|
+
# @example Identity matcher
|
26
|
+
# object = "foo"
|
27
|
+
#
|
28
|
+
# matcher = equal(object) # => Matchi::Matcher::Equal.new(object)
|
29
|
+
# matcher.matches? { object } # => true
|
30
|
+
# matcher.matches? { "foo" } # => false
|
31
|
+
#
|
32
|
+
# matcher = be(object) # => Matchi::Matcher::Be.new(object)
|
33
|
+
# matcher.matches? { object } # => true
|
34
|
+
# matcher.matches? { "foo" } # => false
|
35
|
+
#
|
36
|
+
# @example Regular expressions matcher
|
37
|
+
# matcher = match(/^foo$/) # => Matchi::Matcher::Match.new(/^foo$/)
|
38
|
+
# matcher.matches? { "foo" } # => true
|
39
|
+
# matcher.matches? { "bar" } # => false
|
40
|
+
#
|
41
|
+
# @example Expecting errors matcher
|
42
|
+
# matcher = raise_exception(NameError) # => Matchi::Matcher::RaiseException.new(NameError)
|
43
|
+
# matcher.matches? { Boom } # => true
|
44
|
+
# matcher.matches? { true } # => false
|
45
|
+
#
|
46
|
+
# @example Truth matcher
|
47
|
+
# matcher = be_true # => Matchi::Matcher::BeTrue.new
|
48
|
+
# matcher.matches? { true } # => true
|
49
|
+
# matcher.matches? { false } # => false
|
50
|
+
# matcher.matches? { nil } # => false
|
51
|
+
# matcher.matches? { 4 } # => false
|
52
|
+
#
|
53
|
+
# @example Untruth matcher
|
54
|
+
# matcher = be_false # => Matchi::Matcher::BeFalse.new
|
55
|
+
# matcher.matches? { false } # => true
|
56
|
+
# matcher.matches? { true } # => false
|
57
|
+
# matcher.matches? { nil } # => false
|
58
|
+
# matcher.matches? { 4 } # => false
|
59
|
+
#
|
60
|
+
# @example Nil matcher
|
61
|
+
# matcher = be_nil # => Matchi::Matcher::BeNil.new
|
62
|
+
# matcher.matches? { nil } # => true
|
63
|
+
# matcher.matches? { false } # => false
|
64
|
+
# matcher.matches? { true } # => false
|
65
|
+
# matcher.matches? { 4 } # => false
|
66
|
+
#
|
67
|
+
# @example Type/class matcher
|
68
|
+
# matcher = be_instance_of(String) # => Matchi::Matcher::BeInstanceOf.new(String)
|
69
|
+
# matcher.matches? { "foo" } # => true
|
70
|
+
# matcher.matches? { 4 } # => false
|
71
|
+
#
|
72
|
+
# matcher = be_an_instance_of(String) # => Matchi::Matcher::BeAnInstanceOf.new(String)
|
73
|
+
# matcher.matches? { "foo" } # => true
|
74
|
+
# matcher.matches? { 4 } # => false
|
75
|
+
#
|
76
|
+
# @see https://github.com/fixrb/matchi
|
77
|
+
# @see https://github.com/fixrb/matchi-rspec
|
78
|
+
module Base
|
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
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require_relative File.join("..", "expectation_target")
|
5
|
+
|
6
|
+
module RSpec
|
7
|
+
module ExpectationHelper
|
8
|
+
# `it` expectation helper module.
|
9
|
+
module It
|
10
|
+
include Base
|
11
|
+
|
12
|
+
# Create an expectation for a spec.
|
13
|
+
#
|
14
|
+
# @param value [#object_id, nil] An actual value.
|
15
|
+
# @param block [#call, nil] A code to evaluate.
|
16
|
+
#
|
17
|
+
# @return [Block, Value] The wrapped target of an expectation.
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# expect("foo") # => #<RSpec::ExpectationTarget::Value:0x00007fb6b82311a0 @actual="foo">
|
21
|
+
# expect { Boom } # => #<RSpec::ExpectationTarget::Block:0x00007fb6b8263df8 @callable=#<Proc:0x00007fb6b8263e20>>
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def expect(value = self.class.superclass, &block)
|
25
|
+
ExpectationTarget.call(self.class.superclass, value, block)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Wraps the target of an expectation with the subject as actual value.
|
29
|
+
#
|
30
|
+
# @return (see #expect)
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# is_expected # => #<RSpec::ExpectationTarget::Block:0x00007fb6b8263df8 @callable=#<Proc:0x00007fb6b8263e20>>
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def is_expected
|
37
|
+
expect { subject }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require_relative File.join("..", "expectation_target", "block")
|
5
|
+
|
6
|
+
module RSpec
|
7
|
+
module ExpectationHelper
|
8
|
+
# `its` expectation helper module.
|
9
|
+
module Its
|
10
|
+
include Base
|
11
|
+
|
12
|
+
# Wraps the target of an expectation with the actual value.
|
13
|
+
#
|
14
|
+
# @return [Block] The wrapped target of an expectation.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# is_expected # => #<RSpec::ExpectationTarget::Block:0x00007fb6b8263df8 @callable=#<Proc:0x00007fb6b8263e20>>
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def is_expected
|
21
|
+
ExpectationTarget::Block.new(method(:actual))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,14 +2,16 @@
|
|
2
2
|
|
3
3
|
module RSpec
|
4
4
|
# Wraps the target of an expectation.
|
5
|
+
#
|
6
|
+
# @api private
|
5
7
|
module ExpectationTarget
|
6
8
|
# @param undefined_value A sentinel value to be able to tell when the user
|
7
9
|
# did not pass an argument. We can't use `nil` for that because `nil` is a
|
8
10
|
# valid value to pass.
|
9
|
-
# @param value [#object_id, nil] An actual value
|
11
|
+
# @param value [#object_id, nil] An actual value.
|
10
12
|
# @param block [#call, nil] A code to evaluate.
|
11
13
|
#
|
12
|
-
# @
|
14
|
+
# @return [Block, Value] The wrapped target of an expectation.
|
13
15
|
def self.call(undefined_value, value, block)
|
14
16
|
if undefined_value.equal?(value)
|
15
17
|
raise ::ArgumentError, "Pass either an argument or a block" unless block
|
@@ -4,9 +4,10 @@ require "expresenter"
|
|
4
4
|
|
5
5
|
module RSpec
|
6
6
|
module ExpectationTarget
|
7
|
-
# Abstract class.
|
7
|
+
# Abstract expectation target base class.
|
8
8
|
#
|
9
|
-
# @
|
9
|
+
# @note `RSpec::ExpectationTarget::Base` is not intended to be instantiated
|
10
|
+
# directly by users. Use `expect` instead.
|
10
11
|
class Base
|
11
12
|
# Runs the given expectation, passing if `matcher` returns true.
|
12
13
|
#
|
@@ -17,6 +18,8 @@ module RSpec
|
|
17
18
|
#
|
18
19
|
# @raise (see #result)
|
19
20
|
# @return (see #result)
|
21
|
+
#
|
22
|
+
# @api public
|
20
23
|
def to(matcher)
|
21
24
|
absolute_requirement(matcher: matcher, negate: false)
|
22
25
|
end
|
@@ -30,6 +33,8 @@ module RSpec
|
|
30
33
|
#
|
31
34
|
# @raise (see #result)
|
32
35
|
# @return (see #result)
|
36
|
+
#
|
37
|
+
# @api public
|
33
38
|
def not_to(matcher)
|
34
39
|
absolute_requirement(matcher: matcher, negate: true)
|
35
40
|
end
|
@@ -47,6 +52,8 @@ module RSpec
|
|
47
52
|
#
|
48
53
|
# @raise [SystemExit] Terminate execution immediately by calling
|
49
54
|
# `Kernel.exit(false)` with a failure message written to STDERR.
|
55
|
+
#
|
56
|
+
# @api private
|
50
57
|
def result(actual:, error:, got:, matcher:, negate:, valid:)
|
51
58
|
puts " " + ::Expresenter.call(valid).with(
|
52
59
|
actual: actual,
|
@@ -6,7 +6,7 @@ require_relative "base"
|
|
6
6
|
|
7
7
|
module RSpec
|
8
8
|
module ExpectationTarget
|
9
|
-
# Wraps the target of an expectation.
|
9
|
+
# Wraps the target of an expectation with a block.
|
10
10
|
#
|
11
11
|
# @example
|
12
12
|
# expect { something } # => ExpectationTarget::Block wrapping something
|
@@ -19,14 +19,10 @@ module RSpec
|
|
19
19
|
#
|
20
20
|
# @note `RSpec::ExpectationTarget::Block` is not intended to be instantiated
|
21
21
|
# directly by users. Use `expect` instead.
|
22
|
-
#
|
23
|
-
# @private
|
24
22
|
class Block < Base
|
25
23
|
# Instantiate a new expectation target.
|
26
24
|
#
|
27
25
|
# @param block [#call] The code to evaluate.
|
28
|
-
#
|
29
|
-
# @api private
|
30
26
|
def initialize(block)
|
31
27
|
super()
|
32
28
|
|
@@ -36,10 +32,12 @@ module RSpec
|
|
36
32
|
protected
|
37
33
|
|
38
34
|
# @param matcher [#matches?] The matcher.
|
39
|
-
# @param negate [Boolean]
|
35
|
+
# @param negate [Boolean] The assertion is positive or negative.
|
36
|
+
#
|
37
|
+
# @return [nil] Write a message to STDOUT.
|
40
38
|
#
|
41
|
-
# @raise
|
42
|
-
#
|
39
|
+
# @raise [SystemExit] Terminate execution immediately by calling
|
40
|
+
# `Kernel.exit(false)` with a failure message written to STDERR.
|
43
41
|
def absolute_requirement(matcher:, negate:)
|
44
42
|
exam = ::Spectus::Exam.new(
|
45
43
|
callable: @callable,
|
@@ -4,7 +4,7 @@ require_relative "base"
|
|
4
4
|
|
5
5
|
module RSpec
|
6
6
|
module ExpectationTarget
|
7
|
-
# Wraps the target of an expectation.
|
7
|
+
# Wraps the target of an expectation with a value.
|
8
8
|
#
|
9
9
|
# @example
|
10
10
|
# expect(something) # => ExpectationTarget::Value wrapping something
|
@@ -17,14 +17,10 @@ module RSpec
|
|
17
17
|
#
|
18
18
|
# @note `RSpec::ExpectationTarget::Value` is not intended to be instantiated
|
19
19
|
# directly by users. Use `expect` instead.
|
20
|
-
#
|
21
|
-
# @private
|
22
20
|
class Value < Base
|
23
21
|
# Instantiate a new expectation target.
|
24
22
|
#
|
25
23
|
# @param actual [#object_id] The actual value.
|
26
|
-
#
|
27
|
-
# @api private
|
28
24
|
def initialize(actual)
|
29
25
|
super()
|
30
26
|
|
@@ -34,10 +30,12 @@ module RSpec
|
|
34
30
|
protected
|
35
31
|
|
36
32
|
# @param matcher [#matches?] The matcher.
|
37
|
-
# @param negate [Boolean]
|
33
|
+
# @param negate [Boolean] The assertion is positive or negative.
|
34
|
+
#
|
35
|
+
# @return [nil] Write a message to STDOUT.
|
38
36
|
#
|
39
|
-
# @raise
|
40
|
-
#
|
37
|
+
# @raise [SystemExit] Terminate execution immediately by calling
|
38
|
+
# `Kernel.exit(false)` with a failure message written to STDERR.
|
41
39
|
def absolute_requirement(matcher:, negate:)
|
42
40
|
valid = negate ^ matcher.matches? { @actual }
|
43
41
|
|
data/lib/r_spec/pending.rb
CHANGED
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.beta4
|
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-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: expresenter
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 1.1.
|
33
|
+
version: 1.1.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: 1.1.
|
40
|
+
version: 1.1.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: spectus
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -188,13 +188,16 @@ files:
|
|
188
188
|
- README.md
|
189
189
|
- lib/r_spec.rb
|
190
190
|
- lib/r_spec/dsl.rb
|
191
|
+
- lib/r_spec/expectation_helper.rb
|
192
|
+
- lib/r_spec/expectation_helper/base.rb
|
193
|
+
- lib/r_spec/expectation_helper/it.rb
|
194
|
+
- lib/r_spec/expectation_helper/its.rb
|
191
195
|
- lib/r_spec/expectation_target.rb
|
192
196
|
- lib/r_spec/expectation_target/base.rb
|
193
197
|
- lib/r_spec/expectation_target/block.rb
|
194
198
|
- lib/r_spec/expectation_target/value.rb
|
195
|
-
- lib/r_spec/log.rb
|
196
199
|
- lib/r_spec/pending.rb
|
197
|
-
- lib/r_spec/
|
200
|
+
- lib/r_spec/sandbox.rb
|
198
201
|
homepage: https://r-spec.dev/
|
199
202
|
licenses:
|
200
203
|
- MIT
|
@@ -202,6 +205,7 @@ metadata:
|
|
202
205
|
bug_tracker_uri: https://github.com/cyril/r_spec.rb/issues
|
203
206
|
documentation_uri: https://rubydoc.info/gems/r_spec
|
204
207
|
source_code_uri: https://github.com/cyril/r_spec.rb
|
208
|
+
wiki_uri: https://github.com/cyril/r_spec.rb/wiki
|
205
209
|
post_install_message:
|
206
210
|
rdoc_options: []
|
207
211
|
require_paths:
|
data/lib/r_spec/log.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "expresenter"
|
4
|
-
|
5
|
-
module RSpec
|
6
|
-
# Exception for debugging purpose.
|
7
|
-
class Log < ::NoMethodError
|
8
|
-
# @param message [String] A message to log to the console.
|
9
|
-
#
|
10
|
-
# @return [nil] Write a log message to STDOUT.
|
11
|
-
def self.result(message)
|
12
|
-
puts " " + ::Expresenter.call(true).with(
|
13
|
-
actual: nil,
|
14
|
-
error: new(message),
|
15
|
-
expected: 42,
|
16
|
-
got: nil,
|
17
|
-
matcher: :be,
|
18
|
-
negate: false,
|
19
|
-
level: :MAY,
|
20
|
-
valid: false
|
21
|
-
).colored_string
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|