r_spec 1.0.0.beta3 → 1.0.0.beta4

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: 0d94ad6d7ba01dc04c4bcbe727543524f6a3d83cf69549a34453c1eefc5282e9
4
+ data.tar.gz: 61b3ebe1299e69d7ef1f2c180325e85e1bda05e2347176ae507fc7430cf311ca
5
5
  SHA512:
6
- metadata.gz: 985f437d4b2a9ffa63a1a14f9d588ba44bea017e5374ec50ba718c41497a0e91cc41ea7705928c7d7b38b3bbc11f6b6f114929d2e81ba1191be02b248b7e43bf
7
- data.tar.gz: c525c43c2259150b9122bbf6a84bf61cbcd6323109992a7ce9ddd2248143788cbc6c15bf4b4f231648aadba29a8e1fed80dc068d9e2462a2b903299ec9c1c174
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) 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.svg)
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
11
  [![Inline Docs](https://inch-ci.org/github/cyril/r_spec.rb.svg)](https://inch-ci.org/github/cyril/r_spec.rb)
12
+ [![Documentation](https://img.shields.io/:yard-docs-38c800.svg)](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
- * 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
+ * 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.beta2"
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
+ [![Super DRY example](https://asciinema.org/a/418672.svg)](https://asciinema.org/a/418672?autoplay=1)
72
+
58
73
  ## Usage
59
74
 
60
- To understand how the framework builds and runs tests, here are some correspondences between the DSL syntax and the generated Ruby code.
75
+ ### Anatomy of a spec file
61
76
 
62
- ### `describe` method
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
- Example of specification content:
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
- ```ruby
67
- RSpec.describe String do
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
- Corresponding Ruby code:
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
- ```ruby
74
- module RSpec::Test
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
- ### `context` method
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
- The behavior of the `context` method is exactly the same as `describe`.
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
- ### `subject` method
97
+ For unit tests, it is recommended to follow the conventions for method names:
90
98
 
91
- Example of specification content:
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
- ```ruby
94
- RSpec.describe "Subject" do
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
- Corresponding Ruby code:
105
+ `describe` and `context` take an optional description as argument and a block containing the individual specs or nested groupings.
102
106
 
103
- ```ruby
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
- ### Embedded `describe` method
109
+ Expectations define if the value being tested (_actual_) matches a certain value or specific criteria.
116
110
 
117
- Example of specification content:
111
+ #### Equivalence
118
112
 
119
113
  ```ruby
120
- RSpec.describe "Describe" do
121
- # main describe block
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
- Corresponding Ruby code:
118
+ #### Identity
130
119
 
131
120
  ```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
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
- ### `let` method
146
-
147
- Example of specification content:
125
+ #### Regular expressions
148
126
 
149
127
  ```ruby
150
- RSpec.describe do
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
- Corresponding Ruby code:
131
+ #### Expecting errors
158
132
 
159
133
  ```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
134
+ expect { actual }.to raise_exception(expected) # passes if expected exception is raised
177
135
  ```
178
136
 
179
- ### `before` method
180
-
181
- Example of specification content:
137
+ #### Truth
182
138
 
183
139
  ```ruby
184
- RSpec.describe do
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
- Corresponding Ruby code:
143
+ #### Untruth
192
144
 
193
145
  ```ruby
194
- module RSpec::Test
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
- ### `expect` method
204
-
205
- Example of specification content:
149
+ #### Nil
206
150
 
207
151
  ```ruby
208
- RSpec.describe do
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
- Corresponding Ruby code:
155
+ #### Type/class
214
156
 
215
157
  ```ruby
216
- module RSpec::Test
217
- class Test3582143298
218
- end
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
- example_class = Class.new(RSpec::Test::Test3582143298) do
222
- include Matchi::Helper
162
+ ### Running specs
223
163
 
224
- # Declaration of private methods (`expect`, `is_expected`, `log`, `pending`).
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
- example_class.new.instance_eval do
228
- ExpectationTarget::Value.new(41.next).to be(42)
229
- end
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
- Success: expected to be 42.
177
+ Run a single file:
233
178
 
234
- ## Example
179
+ ```sh
180
+ ruby spec/my/test/file_spec.rb
181
+ ```
235
182
 
236
- Let's test an array:
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
- ```ruby
239
- # array_spec.rb
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
- require "r_spec"
192
+ ### Spec helper
242
193
 
243
- RSpec.describe Array do
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
- describe "#count" do
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
- it { is_expected.to be 0 }
198
+ ### `rake` integration example
254
199
 
255
- context "when a new element is added" do
256
- before do
257
- @elements << 1
258
- end
200
+ The following `Rakefile` settings should be enough:
259
201
 
260
- it { is_expected.to be 1 }
261
- end
262
- end
263
- end
264
- ```
202
+ ```ruby
203
+ require "bundler/gem_tasks"
204
+ require "rake/testtask"
265
205
 
266
- It can be tested in the console with the command:
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
- ```sh
269
- ruby array_spec.rb
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
- # it { expect(41.next).to be 42 }
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
- DSL.describe(const, &block)
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 DSL
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
- # @param block [Proc] The content of the method to define.
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
- # Describe a set of expectations.
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 = Test.const_set(random_test_const_name, ::Class.new(self))
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
- # Evaluate an expectation.
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 = example.new
68
+ i = it_example.new
60
69
  i.instance_eval(&block)
61
70
  end
62
71
 
63
- # @private
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
- # @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
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 :example, :random_test_const_name
130
+ private_class_method :it_example, :its_example, :random_test_const_name
118
131
  end
119
132
  end
120
133
 
121
- require_relative "expectation_target"
122
- require_relative "log"
123
- require_relative "pending"
124
- require_relative "test"
134
+ require_relative "expectation_helper"
135
+ require_relative "sandbox"
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ # Namespace for `it` and `its` helper modules.
5
+ module ExpectationHelper
6
+ end
7
+ end
8
+
9
+ require_relative File.join("expectation_helper", "it")
10
+ require_relative File.join("expectation_helper", "its")
@@ -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
- # @private
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
- # @private
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] Positive or negative assertion?
35
+ # @param negate [Boolean] The assertion is positive or negative.
36
+ #
37
+ # @return [nil] Write a message to STDOUT.
40
38
  #
41
- # @raise (see Base#result)
42
- # @return (see Base#result)
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] Positive or negative assertion?
33
+ # @param negate [Boolean] The assertion is positive or negative.
34
+ #
35
+ # @return [nil] Write a message to STDOUT.
38
36
  #
39
- # @raise (see Base#result)
40
- # @return (see Base#result)
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
 
@@ -4,6 +4,8 @@ require "expresenter"
4
4
 
5
5
  module RSpec
6
6
  # Exception for pending expectations.
7
+ #
8
+ # @api private
7
9
  class Pending < ::NotImplementedError
8
10
  # @param message [String] The not implemented expectation description.
9
11
  #
@@ -2,6 +2,8 @@
2
2
 
3
3
  module RSpec
4
4
  # Namespace to collect test classes.
5
- module Test
5
+ #
6
+ # @api private
7
+ module Sandbox
6
8
  end
7
9
  end
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.beta3
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-05 00:00:00.000000000 Z
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.0
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.0
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/test.rb
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