r_spec 0.3.1 → 1.0.0.beta3

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
- SHA1:
3
- metadata.gz: 9b22b4685a1ce6a25e848c1d954859dd5bbe5edf
4
- data.tar.gz: c1ff7f58da140a017a2fba52ad7139aada7f2355
2
+ SHA256:
3
+ metadata.gz: 7baead9bb63726ef4e29f1ae2df0ff2584177377d1ddda34377d0fb37503ec64
4
+ data.tar.gz: 52ebf75f1e76d238ffc7592fd624e6af5bc2eed3423d5742accfcb3222555560
5
5
  SHA512:
6
- metadata.gz: 5bad34ba991c5253842694094d9ae7bd3608058d272357a756a562470381a853164f020b865aadd53cb04dec78ba00c248c84f1e0a8af68fb69ceae957bbe41b
7
- data.tar.gz: a79b3e7b6e5e216746c4f4bd7ac3f7faa601585f728d95afff65924914c12a603e9e2ef4af18f25002592db79ed09b34be7d71b734ab60d8217fe615b99f870d
6
+ metadata.gz: 985f437d4b2a9ffa63a1a14f9d588ba44bea017e5374ec50ba718c41497a0e91cc41ea7705928c7d7b38b3bbc11f6b6f114929d2e81ba1191be02b248b7e43bf
7
+ data.tar.gz: c525c43c2259150b9122bbf6a84bf61cbcd6323109992a7ce9ddd2248143788cbc6c15bf4b4f231648aadba29a8e1fed80dc068d9e2462a2b903299ec9c1c174
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015 Cyril Kato
3
+ Copyright (c) 2015-2021 Cyril Kato
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,136 +1,298 @@
1
- # R Spec
1
+ # RSpec clone
2
2
 
3
- [![Build Status](https://travis-ci.org/cyril/r_spec.svg?branch=master)][travis]
4
- [![Code Climate](https://codeclimate.com/github/cyril/r_spec/badges/gpa.svg)][codeclimate]
5
- [![Dependency Status](https://gemnasium.com/cyril/r_spec.svg)][gemnasium]
6
- [![Gem Version](https://badge.fury.io/rb/r_spec.svg)][gem]
7
- [![Inline docs](http://inch-ci.org/github/cyril/r_spec.svg?branch=master)][inchpages]
8
- [![Documentation](http://img.shields.io/:yard-docs-38c800.svg)][rubydoc]
3
+ A minimalist [RSpec](https://github.com/rspec/rspec) clone with all the essentials.
9
4
 
10
- > A small [Rspec](https://github.com/rspec/rspec) clone based on [Fix specing framework](https://github.com/fixrb/fix).
5
+ ![What did you expect?](https://github.com/cyril/r_spec.rb/raw/main/img/what-did-you-expect.jpg)
11
6
 
12
- ***
7
+ ## Status
13
8
 
14
- :warning: Important:
9
+ [![Gem Version](https://badge.fury.io/rb/r_spec.svg)](https://badge.fury.io/rb/r_spec)
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)
15
12
 
16
- To avoid confusion in the community, please note that the gem of this project is **not [rspec](https://rubygems.org/gems/rspec)**, it is **[r_spec](https://rubygems.org/gems/r_spec)** (meaning _Ruby Spec_).
13
+ ## Goal
17
14
 
18
- This project is totally independent of [rspec.info](http://rspec.info/).
15
+ This clone attempts to provide most of RSpec's DSL without magic power.
19
16
 
20
- Also, while both gems define an `RSpec` module, **r_spec** (which follows [the gem naming convention](http://guides.rubygems.org/name-your-gem/#use-underscores-for-multiple-words)) is still quite different than **rspec** due to its [Ruby Fix](http://fixrb.org/) dependency.
17
+ ## Some differences
21
18
 
22
- ***
19
+ * Less features and an implementation with much less code complexity.
20
+ * Spec files can also be executed directly with the `ruby` executable.
21
+ * 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
25
 
24
- ## Contact
26
+ ## Important ⚠️
25
27
 
26
- * Home page: https://github.com/cyril/r_spec
27
- * Bugs/issues: https://github.com/cyril/r_spec/issues
28
+ To avoid confusion in the community, please note that:
28
29
 
29
- ## Rubies
30
+ - the gem of this project is **not [`rspec`](https://rubygems.org/gems/rspec)**,
31
+ it is **[`r_spec`](https://rubygems.org/gems/r_spec)**;
32
+ - this project is totally independent of [rspec.info](https://rspec.info/).
30
33
 
31
- * [MRI](https://www.ruby-lang.org/)
32
- * [Rubinius](http://rubini.us/)
33
- * [JRuby](http://jruby.org/)
34
+ ### Note
34
35
 
35
- ## Terminal sessions
36
+ Following [RubyGems naming conventions](https://guides.rubygems.org/name-your-gem/#use-underscores-for-multiple-words), the module name for this project is `RSpec`.
36
37
 
37
- A comparison between the behavior of a small fix-based script (which became the `r_spec` gem) and `rspec`.
38
+ ## Installation
38
39
 
39
- As a result, with `r_spec` the build is passing, while with rspec it is failing. Despite two separate contexts, `rspec` was not able to evaluate some code in isolation to prevent side effects.
40
+ Add this line to your application's Gemfile:
40
41
 
41
- [![What did you RSpec?](https://asciinema.org/a/29070.png)](https://asciinema.org/a/29070)
42
+ ```ruby
43
+ gem "r_spec", ">= 1.0.0.beta2"
44
+ ```
42
45
 
43
- ***
46
+ And then execute:
44
47
 
45
- Unbelievable but true, sometimes "💩" is equal to 42.
48
+ ```sh
49
+ bundle
50
+ ```
46
51
 
47
- Although fun, this _feature_ can be fixed pretty easily...
52
+ Or install it yourself as:
48
53
 
49
- [![Fix RSpec!](https://asciinema.org/a/29172.png)](https://asciinema.org/a/29172)
54
+ ```sh
55
+ gem install r_spec --pre
56
+ ```
50
57
 
51
- ## Installation
58
+ ## Usage
52
59
 
53
- __R Spec__ is cryptographically signed.
60
+ To understand how the framework builds and runs tests, here are some correspondences between the DSL syntax and the generated Ruby code.
54
61
 
55
- To be sure the gem you install hasn't been tampered with, add my public key (if you haven't already) as a trusted certificate:
62
+ ### `describe` method
56
63
 
57
- $ gem cert --add <(curl -Ls https://raw.github.com/cyril/r_spec/master/certs/gem-cyril-public_cert.pem)
58
- $ gem install r_spec -P HighSecurity
64
+ Example of specification content:
59
65
 
60
- The `HighSecurity` trust profile will verify all gems. All of __R Spec__'s dependencies are signed.
66
+ ```ruby
67
+ RSpec.describe String do
68
+ end
69
+ ```
61
70
 
62
- Or add this line to your application's Gemfile:
71
+ Corresponding Ruby code:
63
72
 
64
73
  ```ruby
65
- gem 'r_spec'
74
+ module RSpec::Test
75
+ class Test3582143298
76
+ protected
77
+
78
+ def described_class
79
+ String
80
+ end
81
+ end
82
+ end
66
83
  ```
67
84
 
68
- And then execute:
85
+ ### `context` method
69
86
 
70
- $ bundle
87
+ The behavior of the `context` method is exactly the same as `describe`.
71
88
 
72
- ## Usage
89
+ ### `subject` method
73
90
 
74
- Given this `greeting_spec.rb` spec:
91
+ Example of specification content:
75
92
 
76
93
  ```ruby
77
- require 'r_spec'
94
+ RSpec.describe "Subject" do
95
+ subject do
96
+ :foo
97
+ end
98
+ end
99
+ ```
78
100
 
79
- greeting = 'Hello, world!'
101
+ Corresponding Ruby code:
80
102
 
81
- RSpec.describe 'Test' do
82
- context 'Alice' do
83
- before { greeting.gsub!('world', 'Alice') }
84
- it { expect(greeting).to eql 'Hello, Alice!' }
103
+ ```ruby
104
+ module RSpec::Test
105
+ class Test3582143298
106
+ protected
107
+
108
+ def subject
109
+ :foo
110
+ end
85
111
  end
112
+ end
113
+ ```
114
+
115
+ ### Embedded `describe` method
116
+
117
+ Example of specification content:
86
118
 
87
- context 'Bob' do
88
- before { greeting.gsub!('world', 'Bob') }
89
- it { expect(greeting).to eql 'Hello, Bob!' }
119
+ ```ruby
120
+ RSpec.describe "Describe" do
121
+ # main describe block
122
+
123
+ describe "Embedded describe" do
124
+ # embedded describe block
125
+ end
126
+ end
127
+ ```
128
+
129
+ Corresponding Ruby code:
130
+
131
+ ```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
143
+ ```
144
+
145
+ ### `let` method
146
+
147
+ Example of specification content:
148
+
149
+ ```ruby
150
+ RSpec.describe do
151
+ let(:var0) { 42 }
152
+ let(:var1) { 42 + var3 }
153
+ let(:var3) { 42 }
154
+ end
155
+ ```
156
+
157
+ Corresponding Ruby code:
158
+
159
+ ```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
177
+ ```
178
+
179
+ ### `before` method
180
+
181
+ Example of specification content:
182
+
183
+ ```ruby
184
+ RSpec.describe do
185
+ before do
186
+ puts "hello"
187
+ end
188
+ end
189
+ ```
190
+
191
+ Corresponding Ruby code:
192
+
193
+ ```ruby
194
+ module RSpec::Test
195
+ class Test3582143298
196
+ def initialize
197
+ puts "hello"
198
+ end
199
+ end
200
+ end
201
+ ```
202
+
203
+ ### `expect` method
204
+
205
+ Example of specification content:
206
+
207
+ ```ruby
208
+ RSpec.describe do
209
+ it { expect(41.next).to be(42) }
210
+ end
211
+ ```
212
+
213
+ Corresponding Ruby code:
214
+
215
+ ```ruby
216
+ module RSpec::Test
217
+ class Test3582143298
218
+ end
219
+ end
220
+
221
+ example_class = Class.new(RSpec::Test::Test3582143298) do
222
+ include Matchi::Helper
223
+
224
+ # Declaration of private methods (`expect`, `is_expected`, `log`, `pending`).
225
+ end
226
+
227
+ example_class.new.instance_eval do
228
+ ExpectationTarget::Value.new(41.next).to be(42)
229
+ end
230
+ ```
231
+
232
+ Success: expected to be 42.
233
+
234
+ ## Example
235
+
236
+ Let's test an array:
237
+
238
+ ```ruby
239
+ # array_spec.rb
240
+
241
+ require "r_spec"
242
+
243
+ RSpec.describe Array do
244
+ before do
245
+ @elements = described_class.new
246
+ end
247
+
248
+ describe "#count" do
249
+ subject do
250
+ @elements.count
251
+ end
252
+
253
+ it { is_expected.to be 0 }
254
+
255
+ context "when a new element is added" do
256
+ before do
257
+ @elements << 1
258
+ end
259
+
260
+ it { is_expected.to be 1 }
261
+ end
90
262
  end
91
263
  end
92
264
  ```
93
265
 
94
266
  It can be tested in the console with the command:
95
267
 
96
- $ ruby greeting_spec.rb
97
- ..
268
+ ```sh
269
+ ruby array_spec.rb
270
+ ```
98
271
 
99
- Ran 2 tests in 0.010994 seconds
100
- 100% compliant - 0 infos, 0 failures, 0 errors
272
+ array_spec.rb:15 Success: expected to be 0.
273
+ array_spec.rb:22 Success: expected to be 1.
101
274
 
102
- ## Security
275
+ ## Test suite
103
276
 
104
- As a basic form of security __R Spec__ provides a set of SHA512 checksums for
105
- every Gem release. These checksums can be found in the `checksum/` directory.
106
- Although these checksums do not prevent malicious users from tampering with a
107
- built Gem they can be used for basic integrity verification purposes.
277
+ __RSpec clone__'s specifications are self-described here: [spec/](https://github.com/cyril/r_spec.rb/blob/main/spec/)
108
278
 
109
- The checksum of a file can be checked using the `sha512sum` command. For
110
- example:
279
+ ## Contact
111
280
 
112
- $ sha512sum pkg/r_spec-0.1.0.gem
113
- e9e35e1953104e2d428b0f217e418db3c1baecd9e011b2545f9fcba4ff7e3bba674c6b928b3d8db842a139cd7cc9806d77ebdc7f710ece4f2aecb343703e2451 pkg/r_spec-0.1.0.gem
281
+ * Home page: https://r-spec.dev
282
+ * Source code: https://github.com/cyril/r_spec.rb
283
+ * Twitter: [https://twitter.com/cyri\_](https://twitter.com/cyri\_)
114
284
 
115
285
  ## Versioning
116
286
 
117
- __R Spec__ follows [Semantic Versioning 2.0](http://semver.org/).
287
+ __RSpec clone__ follows [Semantic Versioning 2.0](https://semver.org/).
118
288
 
119
- ## Contributing
289
+ ## Special thanks
120
290
 
121
- 1. [Fork it](https://github.com/cyril/r_spec/fork)
122
- 2. Create your feature branch (`git checkout -b my-new-feature`)
123
- 3. Commit your changes (`git commit -am 'Add some feature'`)
124
- 4. Push to the branch (`git push origin my-new-feature`)
125
- 5. Create a new Pull Request
291
+ I would like to thank the whole [RSpec team](https://rspec.info/about/) for all their work.
292
+ It's a great framework and it's a pleasure to work with every day.
126
293
 
127
- ## License
294
+ Without RSpec, this clone would not have been possible. ❤️
128
295
 
129
- See `LICENSE.md` file.
296
+ ## License
130
297
 
131
- [gem]: https://rubygems.org/gems/r_spec
132
- [travis]: https://travis-ci.org/cyril/r_spec
133
- [codeclimate]: https://codeclimate.com/github/cyril/r_spec
134
- [gemnasium]: https://gemnasium.com/cyril/r_spec
135
- [inchpages]: http://inch-ci.org/github/cyril/r_spec
136
- [rubydoc]: http://rubydoc.info/gems/r_spec/frames
298
+ The [gem](https://rubygems.org/gems/r_spec) is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/lib/r_spec.rb CHANGED
@@ -1,32 +1,25 @@
1
- require 'matchi/rspec'
2
- require 'fix/expect'
3
- require 'fix/its'
4
- require 'fix/let'
1
+ # frozen_string_literal: true
5
2
 
6
- # Namespace for the R Spec framework.
3
+ # Top level namespace for the RSpec clone.
7
4
  #
8
- # @api public
5
+ # @example
6
+ # require "r_spec"
7
+ #
8
+ # RSpec.describe Integer do
9
+ # it { expect(41.next).to be 42 }
10
+ # end
9
11
  #
12
+ # @api public
10
13
  module RSpec
11
14
  # Specs are built with this method.
12
15
  #
13
- # @example The answer must be equal to 42.
14
- # describe('the answer') do
15
- # it { expect(42).to be 42 }
16
- # end
16
+ # @param const [Module, String] A module to include in block context.
17
+ # @param block [Proc] The block to define the specs.
17
18
  #
18
- # @param front_object [#object_id] The front object.
19
- # @param options [Hash] Some options.
20
- # @param specs [Proc] The set of specs.
21
- #
22
- # @raise [SystemExit] The result of the test.
23
- def self.describe(front_object, options = {}, &specs)
24
- t = ::Fix::Test.new(front_object, options, &specs)
25
-
26
- print t.report.to_s if options.fetch(:verbose, true)
27
- exit t.pass?
19
+ # @api public
20
+ def self.describe(const, &block)
21
+ DSL.describe(const, &block)
28
22
  end
29
23
  end
30
24
 
31
- require_relative File.join 'fix', 'it'
32
- require_relative File.join 'fix', 'on'
25
+ require_relative File.join("r_spec", "dsl")
data/lib/r_spec/dsl.rb ADDED
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "matchi/rspec"
4
+ require "securerandom"
5
+
6
+ module RSpec
7
+ # Abstract class for handling the domain-specific language.
8
+ class DSL
9
+ # @param block [Proc] The content to execute at the class initialization.
10
+ def self.before(&block)
11
+ define_method(:initialize) do |*args, **kwargs|
12
+ super()
13
+ instance_exec(*args, **kwargs, &block)
14
+ end
15
+ end
16
+
17
+ # @param block [Proc] The content of the method to define.
18
+ # @return [Symbol] A protected method that define the block content.
19
+ def self.let(name, &block)
20
+ protected define_method(name.to_sym, &block)
21
+ end
22
+
23
+ # @param block [Proc] The subject to set.
24
+ # @return [Symbol] A `subject` method that define the block content.
25
+ def self.subject(&block)
26
+ let(__method__, &block)
27
+ end
28
+
29
+ # Describe a set of expectations.
30
+ #
31
+ # @param const [Module, #object_id] A module to include in block context.
32
+ # @param block [Proc] The block to define the specs.
33
+ def self.describe(const, &block)
34
+ desc = Test.const_set(random_test_const_name, ::Class.new(self))
35
+
36
+ if const.is_a?(::Module)
37
+ desc.define_method(:described_class) { const }
38
+ desc.send(:protected, :described_class)
39
+ end
40
+
41
+ desc.instance_eval(&block)
42
+ desc
43
+ end
44
+
45
+ # Add `context` to the DSL.
46
+ singleton_class.send(:alias_method, :context, :describe)
47
+
48
+ # Evaluate an expectation.
49
+ #
50
+ # @param block [Proc] An expectation to evaluate.
51
+ #
52
+ # @raise (see ExpectationTarget::Base#result)
53
+ # @return (see ExpectationTarget::Base#result)
54
+ def self.it(_name = nil, &block)
55
+ raise ::ArgumentError, "Missing block" unless block
56
+
57
+ puts "\e[37m#{block.source_location.join(':')}\e[0m"
58
+
59
+ i = example.new
60
+ i.instance_eval(&block)
61
+ end
62
+
63
+ # @private
64
+ #
65
+ # @return [Class<DSL>] The class of the example to be tested.
66
+ def self.example
67
+ # Dynamic creation of an example class.
68
+ ::Class.new(self) do
69
+ # Include a collection of matchers.
70
+ include ::Matchi::Helper
71
+
72
+ private
73
+
74
+ # Wraps the target of an expectation with the actual value.
75
+ #
76
+ # @param actual [#object_id] The actual value.
77
+ #
78
+ # @return [ExpectationTarget] The target of the expectation.
79
+ def expect(actual = self.class.superclass, &block)
80
+ ExpectationTarget.call(self.class.superclass, actual, block)
81
+ end
82
+
83
+ # Wraps the target of an expectation with the subject as actual value.
84
+ #
85
+ # @return [ExpectationTarget] (see #expect)
86
+ def is_expected
87
+ expect(subject)
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
107
+ end
108
+ end
109
+
110
+ # @private
111
+ #
112
+ # @return [String] A random constant name for a test class.
113
+ def self.random_test_const_name
114
+ "Test#{::SecureRandom.hex(4).to_i(16)}"
115
+ end
116
+
117
+ private_class_method :example, :random_test_const_name
118
+ end
119
+ end
120
+
121
+ require_relative "expectation_target"
122
+ require_relative "log"
123
+ require_relative "pending"
124
+ require_relative "test"