r_spec 1.0.0.beta3 → 1.0.0.beta8

Sign up to get free protection for your applications and to get access to all the features.
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")