r_spec-clone 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b96788e870f05feb6035cc289a06d274f8e68f1c6dbda8187db392d2ddf58cc5
4
+ data.tar.gz: cecca9a87c7860b87aa2485846dadb23337a5292082e9d71685ed3bb61820968
5
+ SHA512:
6
+ metadata.gz: aedab54edd51616dcb01f83ffe82e68285d3d3585c51cb3dc60c78c8d16230f3a5f78e9a886d678153cab84a6aa2e59b17d4d6b7cdb2301bb4ee3ba7687a8290
7
+ data.tar.gz: f2654fab9bb2b84450c69e1ab94fca926b54551c6de307bb7432ece5aedabd8386b661926f65005f02fa4fbd33bd22c8173226ebe6f57001b747a8e1e74905de
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-2021 Cyril Kato
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,259 @@
1
+ # RSpec clone
2
+
3
+ A minimalist __RSpec clone__ with all the essentials.
4
+
5
+ ![What did you RSpec?](https://github.com/cyril/r_spec-clone.rb/raw/main/img/what-did-you-rspec.jpg)
6
+
7
+ ## Status
8
+
9
+ [![Version](https://img.shields.io/github/v/tag/cyril/r_spec-clone.rb?label=Version&logo=github)](https://github.com/cyril/r_spec-clone.rb/releases)
10
+ [![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?)](https://rubydoc.info/github/cyril/r_spec-clone.rb/main)
11
+ [![CI](https://github.com/cyril/r_spec-clone.rb/workflows/CI/badge.svg?branch=main)](https://github.com/cyril/r_spec-clone.rb/actions?query=workflow%3Aci+branch%3Amain)
12
+ [![RuboCop](https://github.com/cyril/r_spec-clone.rb/workflows/RuboCop/badge.svg?branch=main)](https://github.com/cyril/r_spec-clone.rb/actions?query=workflow%3Arubocop+branch%3Amain)
13
+ [![License](https://img.shields.io/github/license/cyril/r_spec-clone.rb?label=License&logo=github)](https://github.com/cyril/r_spec-clone.rb/raw/main/LICENSE.md)
14
+
15
+ ## Project goals
16
+
17
+ 1. Keep a low level of code complexity and ensure thread safety.
18
+ 2. The interface must translate into atomic and simple Ruby objects.
19
+ 3. Avoid overloading the interface with additional alternative syntaxes.
20
+ 4. Provide most of RSpec's DSL to express expected outcomes of a code example.
21
+
22
+ ## Some differences
23
+
24
+ * Spec files can be executed with `ruby` directly.
25
+ * There is no option to activate monkey-patching.
26
+ * It does not rely on hacks such as [`at_exit` hook](https://blog.arkency.com/2013/06/are-we-abusing-at-exit/) to trigger the tests.
27
+ * Built-in matchers [do not trust _actual_](https://asciinema.org/a/29172?autoplay=1&speed=2) and do not send it messages.
28
+ * If no `subject` has been explicitly determined, none is defined.
29
+ * If no described class is set, `described_class` is undefined instead of `nil`.
30
+ * Expectations cannot be added inside a `before` block.
31
+ * [Arbitrary helper methods](https://relishapp.com/rspec/rspec-core/v/3-10/docs/helper-methods/arbitrary-helper-methods) are not exposed to examples.
32
+ * The `let` method defines a helper method rather than a memoized helper method.
33
+ * The one-liner `is_expected` syntax also works with block expectations.
34
+ * `subject`, `before`, `after` and `let` definitions must come before examples.
35
+ * Each [`context` runs its tests in _isolation_](https://asciinema.org/a/29070?autoplay=1&speed=2) to prevent side effects.
36
+
37
+ ## Installation
38
+
39
+ Add this line to your application's Gemfile:
40
+
41
+ ```ruby
42
+ gem "r_spec-clone", ">= 1.0.0.rc1"
43
+ ```
44
+
45
+ And then execute:
46
+
47
+ ```sh
48
+ bundle
49
+ ```
50
+
51
+ Or install it yourself as:
52
+
53
+ ```sh
54
+ gem install r_spec-clone --pre
55
+ ```
56
+
57
+ ## Overview
58
+
59
+ __RSpec clone__ provides a structure for writing executable examples of how your code should behave.
60
+
61
+ Inspired by RSpec, it includes a domain specific language (DSL) that allows you to write examples in a way similar to plain english.
62
+
63
+ A basic spec looks something like this:
64
+
65
+ [![RSpec clone demo](https://asciinema.org/a/422210.svg)](https://asciinema.org/a/422210?autoplay=1&speed=2)
66
+
67
+ ## Usage
68
+
69
+ ### Anatomy of a spec file
70
+
71
+ To use the `RSpec` module and its DSL, you need to add `require "r_spec/clone"` to your spec files.
72
+ Many projects use a custom spec helper which organizes these includes.
73
+
74
+ Concrete test cases are defined in `it` blocks.
75
+ An optional descriptive string states it's purpose and a block contains the main logic performing the test.
76
+
77
+ 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.
78
+
79
+ An `it` block contains an example that should invoke the code to be tested and define what is expected of it.
80
+ Each example can contain multiple expectations, but it should test only one specific behaviour.
81
+
82
+ To express an expectation, wrap an object or block in `expect`, call `to` (or `not_to`) and pass it a matcher object.
83
+ If the expectation is met, code execution continues.
84
+ Otherwise the example has _failed_ and other code will not be executed.
85
+
86
+ In test files, specs are structured by example groups which are defined by `describe` and `context` sections.
87
+ Typically a top level `describe` defines the outer unit (such as a class) to be tested by the spec.
88
+ Further `describe` sections can be nested within the outer unit to specify smaller units under test (such as individual methods).
89
+
90
+ For unit tests, it is recommended to follow the conventions for method names:
91
+
92
+ * outer `describe` is the name of the class, inner `describe` targets methods;
93
+ * instance methods are prefixed with `#`, class methods with `.`.
94
+
95
+ To establish certain contexts — think _empty array_ versus _array with elements_ — the `context` method may be used to communicate this to the reader.
96
+ Its behavior is slightly different from `describe` because each `context` runs its tests in isolation,
97
+ so side effects caused by testing do not propagate out of contexts.
98
+
99
+ ### Expectations
100
+
101
+ Expectations define if the value being tested (_actual_) matches a certain value or specific criteria.
102
+
103
+ #### Equivalence
104
+
105
+ ```ruby
106
+ expect(actual).to eql(expected) # passes if expected.eql?(actual)
107
+ expect(actual).to eq(expected) # passes if expected.eql?(actual)
108
+ ```
109
+
110
+ #### Identity
111
+
112
+ ```ruby
113
+ expect(actual).to equal(expected) # passes if expected.equal?(actual)
114
+ expect(actual).to be(expected) # passes if expected.equal?(actual)
115
+ ```
116
+
117
+ #### Regular expressions
118
+
119
+ ```ruby
120
+ expect(actual).to match(expected) # passes if expected.match?(actual)
121
+ ```
122
+
123
+ #### Expecting errors
124
+
125
+ ```ruby
126
+ expect { actual }.to raise_exception(expected) # passes if expected exception is raised
127
+ ```
128
+
129
+ #### Truth
130
+
131
+ ```ruby
132
+ expect(actual).to be_true # passes if true.equal?(actual)
133
+ ```
134
+
135
+ #### Untruth
136
+
137
+ ```ruby
138
+ expect(actual).to be_false # passes if false.equal?(actual)
139
+ ```
140
+
141
+ #### Nil
142
+
143
+ ```ruby
144
+ expect(actual).to be_nil # passes if nil.equal?(actual)
145
+ ```
146
+
147
+ #### Type/class
148
+
149
+ ```ruby
150
+ expect(actual).to be_instance_of(expected) # passes if expected.equal?(actual.class)
151
+ expect(actual).to be_an_instance_of(expected) # passes if expected.equal?(actual.class)
152
+ ```
153
+
154
+ ### Running specs
155
+
156
+ By convention, specs live in the `spec/` directory of a project. Spec files should end with `_spec.rb` to be recognizable as such.
157
+
158
+ Depending of the project settings, you may run the specs of a project by running `rake spec` (see _Rake integration example_ section below).
159
+ A single file can also be executed directly with the Ruby interpreter.
160
+
161
+ #### Examples
162
+
163
+ Run all specs in files matching `spec/**/*_spec.rb`:
164
+
165
+ ```sh
166
+ bundle exec rake spec
167
+ ```
168
+
169
+ Run a single file:
170
+
171
+ ```sh
172
+ ruby spec/my/test/file_spec.rb
173
+ ```
174
+
175
+ It is not recommended, but the RSpec's [`rspec` command line](https://relishapp.com/rspec/rspec-core/docs/command-line) might also work:
176
+
177
+ ```sh
178
+ rspec spec/my/test/file_spec.rb
179
+ rspec spec/my/test/file_spec.rb:42
180
+ rspec spec/my/test/
181
+ rspec
182
+ ```
183
+
184
+ ### Spec helper
185
+
186
+ Many projects use a custom spec helper file, usually named `spec/spec_helper.rb`.
187
+
188
+ This file is used to require `r_spec/clone` and other includes, like the code from the project needed for every spec file.
189
+
190
+ ### Rake integration example
191
+
192
+ The following `Rakefile` settings should be enough:
193
+
194
+ ```ruby
195
+ require "bundler/gem_tasks"
196
+ require "rake/testtask"
197
+
198
+ Rake::TestTask.new do |t|
199
+ t.pattern = "spec/**/*_spec.rb"
200
+ end
201
+
202
+ task spec: :test
203
+ task default: :test
204
+ ```
205
+
206
+ And then execute:
207
+
208
+ ```sh
209
+ bundle exec rake
210
+ ```
211
+
212
+ ## Performance
213
+
214
+ ### Runtime
215
+
216
+ Benchmark against [100 executions of a file containing one expectation](https://github.com/cyril/r_spec-clone.rb/blob/main/benchmark/) (lower is better).
217
+
218
+ ![Runtime](https://clone.r-spec.dev/benchmark-runtime.png)
219
+
220
+ ## Test suite
221
+
222
+ __RSpec clone__'s specifications are self-described here: [spec/](https://github.com/cyril/r_spec-clone.rb/blob/main/spec/)
223
+
224
+ ## Contact
225
+
226
+ * Home page: [https://r-spec.dev/](https://r-spec.dev/)
227
+ * Cheatsheet: [https://clone.r-spec.dev/cheatsheet.html](https://clone.r-spec.dev/cheatsheet.html)
228
+ * Source code: [https://github.com/cyril/r_spec-clone.rb](https://github.com/cyril/r_spec-clone.rb)
229
+ * API Doc: [https://rubydoc.info/gems/r_spec-clone](https://rubydoc.info/gems/r_spec-clone)
230
+ * Twitter: [https://twitter.com/cyri\_](https://twitter.com/cyri\_)
231
+
232
+ ## Special thanks ❤️
233
+
234
+ I would like to thank the whole [RSpec team](https://rspec.info/about/) for all their work.
235
+ It's a great framework and it's a pleasure to work with every day.
236
+
237
+ Without RSpec, this clone would not have been possible.
238
+
239
+ ## Buy me a coffee ☕
240
+
241
+ If you like this project, please consider making a small donation to Batman.
242
+
243
+ [![Donate](https://img.shields.io/badge/Donate-batman.eth-purple.svg)](https://etherscan.io/address/batman.eth)
244
+
245
+ ## Versioning
246
+
247
+ __RSpec clone__ follows [Semantic Versioning 2.0](https://semver.org/).
248
+
249
+ ## License
250
+
251
+ The [gem](https://rubygems.org/gems/r_spec-clone) is available as open source under the terms of the [MIT License](https://github.com/cyril/r_spec-clone.rb/raw/main/LICENSE.md).
252
+
253
+ ## One more thing
254
+
255
+ Under the hood, __RSpec clone__ is largely animated by [a collection of testing libraries designed to make programmers happy](https://github.com/fixrb/).
256
+
257
+ It's a living example of what we can do combining small libraries together that can boost the fun of programming.
258
+
259
+ ![Fix testing tools logo for Ruby](https://github.com/cyril/r_spec-clone.rb/raw/main/img/fixrb.svg)
data/lib/r_spec.rb ADDED
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative File.join("r_spec", "clone", "dsl")
4
+
5
+ # Top level namespace for the RSpec clone.
6
+ #
7
+ # @example The true from the false
8
+ # require "r_spec/clone"
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/clone"
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
44
+ # require "r_spec/clone"
45
+ #
46
+ # RSpec.describe Integer do
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
60
+ # end
61
+ #
62
+ # # Output to the console
63
+ # # Success: expected to be 42.
64
+ # # Success: expected to be 43.
65
+ #
66
+ # @api public
67
+ module RSpec
68
+ # Defines an example group that establishes a specific context, like _empty
69
+ # array_ versus _array with elements_.
70
+ #
71
+ # Unlike {.describe}, the block is evaluated in isolation in order to scope
72
+ # possible side effects inside its context.
73
+ #
74
+ # @example
75
+ # require "r_spec/clone"
76
+ #
77
+ # RSpec.context "when divided by zero" do
78
+ # subject { 42 / 0 }
79
+ #
80
+ # it { is_expected.to raise_exception ZeroDivisionError }
81
+ # end
82
+ #
83
+ # # Output to the console
84
+ # # Success: divided by 0.
85
+ #
86
+ # @param description [String] A description that usually begins with "when",
87
+ # "with" or "without".
88
+ # @param block [Proc] The block to define the specs.
89
+ #
90
+ # @api public
91
+ def self.context(description, &block)
92
+ Clone::Dsl.context(description, &block)
93
+ end
94
+
95
+ # Defines an example group that describes a unit to be tested.
96
+ #
97
+ # @example
98
+ # require "r_spec/clone"
99
+ #
100
+ # RSpec.describe String do
101
+ # describe "+" do
102
+ # it("concats") { expect("foo" + "bar").to eq "foobar" }
103
+ # end
104
+ # end
105
+ #
106
+ # # Output to the console
107
+ # # Success: expected to eq "foobar".
108
+ #
109
+ # @param const [Module, String] A module to include in block context.
110
+ # @param block [Proc] The block to define the specs.
111
+ #
112
+ # @api public
113
+ def self.describe(const, &block)
114
+ Clone::Dsl.describe(const, &block)
115
+ end
116
+
117
+ # Defines a concrete test case.
118
+ #
119
+ # The test is performed by the block supplied to &block.
120
+ #
121
+ # @example The integer after 41
122
+ # require "r_spec/clone"
123
+ #
124
+ # RSpec.it { expect(41.next).to be 42 }
125
+ #
126
+ # # Output to the console
127
+ # # Success: expected to be 42.
128
+ #
129
+ # It is usually used inside a {Clone::Dsl.describe} or {Clone::Dsl.context}
130
+ # section.
131
+ #
132
+ # @param name [String, nil] The name of the spec.
133
+ # @param block [Proc] An expectation to evaluate.
134
+ #
135
+ # @raise (see RSpec::ExpectationTarget::Base#result)
136
+ # @return (see RSpec::ExpectationTarget::Base#result)
137
+ #
138
+ # @api public
139
+ def self.it(name = nil, &block)
140
+ Clone::Dsl.it(name, &block)
141
+ end
142
+
143
+ # Defines a pending test case.
144
+ #
145
+ # `&block` is never evaluated. It can be used to describe behaviour that is
146
+ # not yet implemented.
147
+ #
148
+ # @example
149
+ # require "r_spec/clone"
150
+ #
151
+ # RSpec.pending "is implemented but waiting" do
152
+ # expect something to be finished
153
+ # end
154
+ #
155
+ # RSpec.pending "is not yet implemented and waiting"
156
+ #
157
+ # # Output to the console
158
+ # # Warning: is implemented but waiting.
159
+ # # Warning: is not yet implemented and waiting.
160
+ #
161
+ # It is usually used inside a {Clone::Dsl.describe} or {Clone::Dsl.context}
162
+ # section.
163
+ #
164
+ # @param message [String] The reason why the example is pending.
165
+ #
166
+ # @return [nil] Write a message to STDOUT.
167
+ #
168
+ # @api public
169
+ def self.pending(message)
170
+ Clone::Dsl.pending(message)
171
+ end
172
+ end