r_spec 1.0.0.beta3 → 1.0.0.beta8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7baead9bb63726ef4e29f1ae2df0ff2584177377d1ddda34377d0fb37503ec64
4
- data.tar.gz: 52ebf75f1e76d238ffc7592fd624e6af5bc2eed3423d5742accfcb3222555560
3
+ metadata.gz: 654594b32d5ef5c97f68de1a5f3e99cc38364de1703f747d841fccba725b878c
4
+ data.tar.gz: 11038ae3f4f56cf40a7e2d99077ce28e7ee239e36d775dcfe8035954df240e07
5
5
  SHA512:
6
- metadata.gz: 985f437d4b2a9ffa63a1a14f9d588ba44bea017e5374ec50ba718c41497a0e91cc41ea7705928c7d7b38b3bbc11f6b6f114929d2e81ba1191be02b248b7e43bf
7
- data.tar.gz: c525c43c2259150b9122bbf6a84bf61cbcd6323109992a7ce9ddd2248143788cbc6c15bf4b4f231648aadba29a8e1fed80dc068d9e2462a2b903299ec9c1c174
6
+ metadata.gz: 91f106a5823915d56c59edf1a7da73285fdf05ef38827368cc44c135c2973b03aa4e3fe013a2e423ffd7bfdc2bd3f24ccd208f6f8e11fddeba3e95bfe800d7cb
7
+ data.tar.gz: e25a4dc18ba5ffb7aabb0bbe42dc9ef6b88756e4fb825fa895683e181a1ac05a61d153da05deb7c5142473704f2174b0a5a4e75dd612ed0481b2402a0c15a350
data/README.md CHANGED
@@ -1,27 +1,34 @@
1
1
  # RSpec clone
2
2
 
3
- A minimalist [RSpec](https://github.com/rspec/rspec) clone with all the essentials.
3
+ A minimalist __[RSpec](https://github.com/rspec/rspec) clone__ with all the essentials.
4
4
 
5
- ![What did you expect?](https://github.com/cyril/r_spec.rb/raw/main/img/what-did-you-expect.jpg)
5
+ ![What did you RSpec?](https://github.com/cyril/r_spec.rb/raw/main/img/what-did-you-rspec.jpg)
6
6
 
7
7
  ## Status
8
8
 
9
9
  [![Gem Version](https://badge.fury.io/rb/r_spec.svg)](https://badge.fury.io/rb/r_spec)
10
10
  [![Build Status](https://travis-ci.org/cyril/r_spec.rb.svg?branch=main)](https://travis-ci.org/cyril/r_spec.rb)
11
- [![Inline Docs](https://inch-ci.org/github/cyril/r_spec.rb.svg)](https://inch-ci.org/github/cyril/r_spec.rb)
11
+ [![Documentation](https://img.shields.io/:yard-docs-38c800.svg)](https://rubydoc.info/gems/r_spec/frames)
12
12
 
13
- ## Goal
13
+ ## Project goals
14
14
 
15
- This clone attempts to provide most of RSpec's DSL without magic power.
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.
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
- * Does not rely on hacks such as `at_exit` hook to trigger the tests.
23
- * Built-in matchers do not trust _actual_ and do not send it any message.
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
+ * If no `subject` has been explicitly determined, none is defined.
26
+ * If no described class is set, `described_class` is undefined instead of `nil`.
27
+ * Expectations cannot be added inside a `before` block.
28
+ * [Arbitrary helper methods](https://relishapp.com/rspec/rspec-core/v/3-10/docs/helper-methods/arbitrary-helper-methods) are not exposed to examples.
29
+ * The `let` method defines a helper method rather than a memoized helper method.
30
+ * The one-liner `is_expected` syntax also works with block expectations.
31
+ * `subject`, `before`, `after` and `let` definitions must come before examples.
25
32
 
26
33
  ## Important ⚠️
27
34
 
@@ -40,7 +47,7 @@ Following [RubyGems naming conventions](https://guides.rubygems.org/name-your-ge
40
47
  Add this line to your application's Gemfile:
41
48
 
42
49
  ```ruby
43
- gem "r_spec", ">= 1.0.0.beta2"
50
+ gem "r_spec", ">= 1.0.0.beta8"
44
51
  ```
45
52
 
46
53
  And then execute:
@@ -55,222 +62,170 @@ Or install it yourself as:
55
62
  gem install r_spec --pre
56
63
  ```
57
64
 
65
+ ## Overview
66
+
67
+ __RSpec clone__ provides a structure for writing executable examples of how your code should behave.
68
+
69
+ 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.
70
+
71
+ A basic spec looks something like this:
72
+
73
+ [![Super DRY example](https://asciinema.org/a/418672.svg)](https://asciinema.org/a/418672?autoplay=1)
74
+
58
75
  ## Usage
59
76
 
60
- To understand how the framework builds and runs tests, here are some correspondences between the DSL syntax and the generated Ruby code.
77
+ ### Anatomy of a spec file
61
78
 
62
- ### `describe` method
79
+ To use the `RSpec` module and its DSL, you need to add `require "r_spec"` to your spec files.
80
+ Many projects use a custom spec helper which organizes these includes.
63
81
 
64
- Example of specification content:
82
+ Concrete test cases are defined in `it` blocks.
83
+ An optional descriptive string states it's purpose and a block contains the main logic performing the test.
65
84
 
66
- ```ruby
67
- RSpec.describe String do
68
- end
69
- ```
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.
70
86
 
71
- Corresponding Ruby code:
87
+ An `it` block contains an example that should invoke the code to be tested and define what is expected of it.
88
+ Each example can contain multiple expectations, but it should test only one specific behaviour.
72
89
 
73
- ```ruby
74
- module RSpec::Test
75
- class Test3582143298
76
- protected
77
-
78
- def described_class
79
- String
80
- end
81
- end
82
- end
83
- ```
90
+ To express an expectation, wrap an object or block in `expect`, call `to` (or `not_to`) and pass it a matcher object.
91
+ If the expectation is met, code execution continues.
92
+ Otherwise the example has _failed_ and other code will not be executed.
84
93
 
85
- ### `context` method
94
+ In test files, specs are structured by example groups which are defined by `describe` and `context` sections.
95
+ Typically a top level `describe` defines the outer unit (such as a class) to be tested by the spec.
96
+ Further `describe` sections can be nested within the outer unit to specify smaller units under test (such as individual methods).
86
97
 
87
- The behavior of the `context` method is exactly the same as `describe`.
98
+ For unit tests, it is recommended to follow the conventions for method names:
88
99
 
89
- ### `subject` method
100
+ * outer `describe` is the name of the class, inner `describe` targets methods;
101
+ * instance methods are prefixed with `#`, class methods with `.`.
90
102
 
91
- Example of specification content:
103
+ To establish certain contexts — think _empty array_ versus _array with elements_ — the `context` method may be used to communicate this to the reader.
104
+ It has a different name, but behaves exactly like `describe`.
92
105
 
93
- ```ruby
94
- RSpec.describe "Subject" do
95
- subject do
96
- :foo
97
- end
98
- end
99
- ```
106
+ `describe` and `context` take an optional description as argument and a block containing the individual specs or nested groupings.
107
+
108
+ ### Expectations
100
109
 
101
- Corresponding Ruby code:
110
+ Expectations define if the value being tested (_actual_) matches a certain value or specific criteria.
111
+
112
+ #### Equivalence
102
113
 
103
114
  ```ruby
104
- module RSpec::Test
105
- class Test3582143298
106
- protected
107
-
108
- def subject
109
- :foo
110
- end
111
- end
112
- end
115
+ expect(actual).to eql(expected) # passes if expected.eql?(actual)
116
+ expect(actual).to eq(expected) # passes if expected.eql?(actual)
113
117
  ```
114
118
 
115
- ### Embedded `describe` method
116
-
117
- Example of specification content:
119
+ #### Identity
118
120
 
119
121
  ```ruby
120
- RSpec.describe "Describe" do
121
- # main describe block
122
-
123
- describe "Embedded describe" do
124
- # embedded describe block
125
- end
126
- end
122
+ expect(actual).to equal(expected) # passes if expected.equal?(actual)
123
+ expect(actual).to be(expected) # passes if expected.equal?(actual)
127
124
  ```
128
125
 
129
- Corresponding Ruby code:
126
+ #### Regular expressions
130
127
 
131
128
  ```ruby
132
- module RSpec::Test
133
- class Test3582143298
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
129
+ expect(actual).to match(expected) # passes if expected.match?(actual)
143
130
  ```
144
131
 
145
- ### `let` method
146
-
147
- Example of specification content:
132
+ #### Expecting errors
148
133
 
149
134
  ```ruby
150
- RSpec.describe do
151
- let(:var0) { 42 }
152
- let(:var1) { 42 + var3 }
153
- let(:var3) { 42 }
154
- end
135
+ expect { actual }.to raise_exception(expected) # passes if expected exception is raised
155
136
  ```
156
137
 
157
- Corresponding Ruby code:
138
+ #### Truth
158
139
 
159
140
  ```ruby
160
- module RSpec::Test
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
141
+ expect(actual).to be_true # passes if true.equal?(actual)
177
142
  ```
178
143
 
179
- ### `before` method
180
-
181
- Example of specification content:
144
+ #### Untruth
182
145
 
183
146
  ```ruby
184
- RSpec.describe do
185
- before do
186
- puts "hello"
187
- end
188
- end
147
+ expect(actual).to be_false # passes if false.equal?(actual)
189
148
  ```
190
149
 
191
- Corresponding Ruby code:
150
+ #### Nil
192
151
 
193
152
  ```ruby
194
- module RSpec::Test
195
- class Test3582143298
196
- def initialize
197
- puts "hello"
198
- end
199
- end
200
- end
153
+ expect(actual).to be_nil # passes if nil.equal?(actual)
201
154
  ```
202
155
 
203
- ### `expect` method
204
-
205
- Example of specification content:
156
+ #### Type/class
206
157
 
207
158
  ```ruby
208
- RSpec.describe do
209
- it { expect(41.next).to be(42) }
210
- end
159
+ expect(actual).to be_instance_of(expected) # passes if expected.equal?(actual.class)
160
+ expect(actual).to be_an_instance_of(expected) # passes if expected.equal?(actual.class)
211
161
  ```
212
162
 
213
- Corresponding Ruby code:
163
+ ### Running specs
214
164
 
215
- ```ruby
216
- module RSpec::Test
217
- class Test3582143298
218
- end
219
- end
165
+ By convention, specs live in the `spec/` directory of a project. Spec files should end with `_spec.rb` to be recognizable as such.
220
166
 
221
- example_class = Class.new(RSpec::Test::Test3582143298) do
222
- include Matchi::Helper
167
+ 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).
168
+ A single file can also be executed directly with the Ruby interpreter.
223
169
 
224
- # Declaration of private methods (`expect`, `is_expected`, `log`, `pending`).
225
- end
170
+ #### Examples
226
171
 
227
- example_class.new.instance_eval do
228
- ExpectationTarget::Value.new(41.next).to be(42)
229
- end
172
+ Run all specs in files matching `spec/**/*_spec.rb`:
173
+
174
+ ```sh
175
+ bundle exec rake spec
230
176
  ```
231
177
 
232
- Success: expected to be 42.
178
+ Run a single file:
233
179
 
234
- ## Example
180
+ ```sh
181
+ ruby spec/my/test/file_spec.rb
182
+ ```
235
183
 
236
- Let's test an array:
184
+ 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
185
 
238
- ```ruby
239
- # array_spec.rb
186
+ ```sh
187
+ rspec spec/my/test/file_spec.rb
188
+ rspec spec/my/test/file_spec.rb:42
189
+ rspec spec/my/test/
190
+ rspec
191
+ ```
240
192
 
241
- require "r_spec"
193
+ ### Spec helper
242
194
 
243
- RSpec.describe Array do
244
- before do
245
- @elements = described_class.new
246
- end
195
+ Many projects use a custom spec helper file, usually named `spec/spec_helper.rb`.
247
196
 
248
- describe "#count" do
249
- subject do
250
- @elements.count
251
- end
197
+ This file is used to require `r_spec` and other includes, like the code from the project needed for every spec file.
252
198
 
253
- it { is_expected.to be 0 }
199
+ ### `rake` integration example
254
200
 
255
- context "when a new element is added" do
256
- before do
257
- @elements << 1
258
- end
201
+ The following `Rakefile` settings should be enough:
259
202
 
260
- it { is_expected.to be 1 }
261
- end
262
- end
203
+ ```ruby
204
+ require "bundler/gem_tasks"
205
+ require "rake/testtask"
206
+
207
+ Rake::TestTask.new do |t|
208
+ t.pattern = "spec/**/*_spec.rb"
209
+ t.verbose = true
210
+ t.warning = true
263
211
  end
212
+
213
+ task spec: :test
214
+ task default: :test
264
215
  ```
265
216
 
266
- It can be tested in the console with the command:
217
+ ## Performance
267
218
 
268
- ```sh
269
- ruby array_spec.rb
270
- ```
219
+ ### Runtime
271
220
 
272
- array_spec.rb:15 Success: expected to be 0.
273
- array_spec.rb:22 Success: expected to be 1.
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 |
274
229
 
275
230
  ## Test suite
276
231
 
@@ -282,16 +237,22 @@ __RSpec clone__'s specifications are self-described here: [spec/](https://github
282
237
  * Source code: https://github.com/cyril/r_spec.rb
283
238
  * Twitter: [https://twitter.com/cyri\_](https://twitter.com/cyri\_)
284
239
 
285
- ## Versioning
286
-
287
- __RSpec clone__ follows [Semantic Versioning 2.0](https://semver.org/).
288
-
289
- ## Special thanks
240
+ ## Special thanks ❤️
290
241
 
291
242
  I would like to thank the whole [RSpec team](https://rspec.info/about/) for all their work.
292
243
  It's a great framework and it's a pleasure to work with every day.
293
244
 
294
- 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
+ [![Donate with Ethereum](https://github.com/cyril/r_spec.rb/raw/main/img/donate-eth.svg)](https://etherscan.io/address/0x834b5c1feaff5aebf9cd0f25dc38e741d65ab773)
252
+
253
+ ## Versioning
254
+
255
+ __RSpec clone__ follows [Semantic Versioning 2.0](https://semver.org/).
295
256
 
296
257
  ## License
297
258
 
data/lib/r_spec.rb CHANGED
@@ -1,14 +1,68 @@
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
- # @example
7
+ # @example The true from the false
8
+ # require "r_spec"
9
+ #
10
+ # RSpec.describe "The true from the false" do
11
+ # it { expect(false).not_to be true }
12
+ # end
13
+ #
14
+ # # Output to the console
15
+ # # Success: expected false not to be true.
16
+ #
17
+ # @example The basic behavior of arrays
18
+ # require "r_spec"
19
+ #
20
+ # RSpec.describe Array do
21
+ # describe "#size" do
22
+ # it "correctly reports the number of elements in the Array" do
23
+ # expect([1, 2, 3].size).to eq 3
24
+ # end
25
+ # end
26
+ #
27
+ # describe "#empty?" do
28
+ # it "is empty when no elements are in the array" do
29
+ # expect([].empty?).to be_true
30
+ # end
31
+ #
32
+ # it "is not empty if there are elements in the array" do
33
+ # expect([1].empty?).to be_false
34
+ # end
35
+ # end
36
+ # end
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
+ #
43
+ # @example An inherited definition of let
6
44
  # require "r_spec"
7
45
  #
8
46
  # RSpec.describe Integer do
9
- # it { expect(41.next).to be 42 }
47
+ # let(:answer) { 42 }
48
+ #
49
+ # it "returns the value" do
50
+ # expect(answer).to be(42)
51
+ # end
52
+ #
53
+ # context "when the number is incremented" do
54
+ # let(:answer) { super().next }
55
+ #
56
+ # it "returns the next value" do
57
+ # expect(answer).to be(43)
58
+ # end
59
+ # end
10
60
  # end
11
61
  #
62
+ # # Output to the console
63
+ # # Success: expected to be 42.
64
+ # # Success: expected to be 43.
65
+ #
12
66
  # @api public
13
67
  module RSpec
14
68
  # Specs are built with this method.
@@ -18,8 +72,6 @@ module RSpec
18
72
  #
19
73
  # @api public
20
74
  def self.describe(const, &block)
21
- DSL.describe(const, &block)
75
+ Dsl.describe(const, &block)
22
76
  end
23
77
  end
24
-
25
- require_relative File.join("r_spec", "dsl")