spectus 3.3.4 → 3.4.0

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: 8767313f403c0f015d1316eabbf06f124ef4ecb136f8edf3529d533602622a8c
4
- data.tar.gz: 419a5c0e93edac33135d305e9e384fea7d6ca305d68f9bd268f89d4ead020d3c
3
+ metadata.gz: 2fea4a88991addc0a6b69ce46d6422e92df818447818975d6123848cbba9d5a7
4
+ data.tar.gz: 8dffb97e6e58cdf0c3e5661294238e42f5477f4e677a34868ce918156086cbdb
5
5
  SHA512:
6
- metadata.gz: 368fb8f488627e6c5b225bd66416ce4a3c8f61aaab0ce0a68ec6d2f354978393299399ccc4f9ea2c8e719b336764b9b7cededa4f6f237a7e5ad9a1495deb47ef
7
- data.tar.gz: a5e56d1e3c6058f6df75bea0b5d564b0eb2cb6eea620ac9d445bea29a0a45e17302cdddf7cb9b5f7d18609c0b1334527f2495eef264bfb5d9041da453ec1055e
6
+ metadata.gz: 9a4798a255dcf0097dcba1bac46af91552a11348513c0d4af77a00a748cb950e90b113c264404cc745af3c7dc730154ad81893d879a495450f6cda5ca4b07942
7
+ data.tar.gz: ba56345d554c0483ce27fd6c7d876e7837d59a53a37a9b9e6417df849e0a6047b522a2c00e7f5d4327a76c1ed3e60cea68cff5970e7dcc4718d31ed89e3e6fa4
data/README.md CHANGED
@@ -38,7 +38,7 @@ qualifying it with `MUST`, `SHOULD` and `MAY`, we can draw up several scenarios:
38
38
  | Implemented & Exception | `false` | `false` | `false` |
39
39
  | Not implemented | `false` | `false` | `true` |
40
40
 
41
- When an expectation is evaluated by Spectus,
41
+ When an expectation is evaluated by __Spectus__,
42
42
 
43
43
  * in case of a _passed_ expectation, a `Spectus::Result::Pass` instance is _returned_;
44
44
  * in case of a _failed_ expectation, a `Spectus::Result::Fail` exception is _raised_.
@@ -78,11 +78,11 @@ end
78
78
  ```ruby
79
79
  t = Spec.new("foo")
80
80
 
81
- t.test_a # => Spectus::Result::Pass(actual: "FOO", error: nil, expected: "FOO", got: true, matcher: :eql, negate: false, level: :MUST, valid: true)
81
+ t.test_a # => Spectus::Result::Pass(actual: "FOO", error: nil, expected: "FOO", got: true, matcher: :eql, negate: false, level: :MUST)
82
82
 
83
- t.test_b # => Spectus::Result::Pass(actual: nil, error: #<NoMethodError: undefined method `blank?' for "foo":String>, expected: nil, got: nil, matcher: :be_true, negate: false, level: :MAY, valid: false)
83
+ t.test_b # => Spectus::Result::Pass(actual: nil, error: #<NoMethodError: undefined method `blank?' for "foo":String>, expected: nil, got: nil, matcher: :be_true, negate: false, level: :MAY)
84
84
 
85
- t.test_c # => Spectus::Result::Pass(actual: 3, error: nil, expected: 42, got: false, matcher: :equal, negate: false, level: :SHOULD, valid: false)
85
+ t.test_c # => Spectus::Result::Pass(actual: 3, error: nil, expected: 42, got: false, matcher: :equal, negate: false, level: :SHOULD)
86
86
  ```
87
87
 
88
88
  ```ruby
@@ -90,104 +90,87 @@ t = Spec.new(4)
90
90
 
91
91
  t.test_a # => raises an exception:
92
92
  # Traceback (most recent call last):
93
- # 6: from ./bin/console:8:in `<main>'
94
- # 5: from (irb):23
95
- # 4: from (irb):11:in `test_a'
96
- # 3: from /Users/cyril/github/fixrb/spectus/lib/spectus/expectation_target.rb:34:in `MUST'
97
- # 2: from /Users/cyril/github/fixrb/spectus/lib/spectus/requirement_level/base.rb:38:in `call'
98
- # 1: from /Users/cyril/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/expresenter-1.2.1/lib/expresenter/fail.rb:19:in `with'
93
+ # 3: from ./bin/console:8:in `<main>'
94
+ # 2: from (irb):23
95
+ # 1: from (irb):11:in `test_a'
99
96
  # Spectus::Result::Fail (NoMethodError: undefined method `upcase' for 4:Integer)
100
97
 
101
- t.test_b # => Spectus::Result::Pass(actual: nil, error: #<NoMethodError: undefined method `blank?' for 4:Integer>, expected: nil, got: nil, matcher: :be_true, negate: false, level: :MAY, valid: false)
98
+ t.test_b # => Spectus::Result::Pass(actual: nil, error: #<NoMethodError: undefined method `blank?' for 4:Integer>, expected: nil, got: nil, matcher: :be_true, negate: false, level: :MAY)
102
99
 
103
100
  t.test_c # => raises an exception:
104
101
  # Traceback (most recent call last):
105
- # 6: from ./bin/console:8:in `<main>'
106
- # 5: from (irb):25
107
- # 4: from (irb):19:in `test_c'
108
- # 3: from /Users/cyril/github/fixrb/spectus/lib/spectus/expectation_target.rb:100:in `SHOULD'
109
- # 2: from /Users/cyril/github/fixrb/spectus/lib/spectus/requirement_level/base.rb:38:in `call'
110
- # 1: from /Users/cyril/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/expresenter-1.2.1/lib/expresenter/fail.rb:19:in `with'
102
+ # 3: from ./bin/console:8:in `<main>'
103
+ # 2: from (irb):25
104
+ # 1: from (irb):19:in `test_c'
111
105
  # Spectus::Result::Fail (NoMethodError: undefined method `length' for 4:Integer.)
112
106
  ```
113
107
 
114
108
  ## More examples
115
109
 
116
- ### Absolute Requirement
117
-
118
- Given the "`ルビー`" object, when it receives `valid_encoding?` method, then it **MUST** be `true`:
110
+ To make __Spectus__ available:
119
111
 
120
112
  ```ruby
121
113
  require "spectus"
122
114
 
123
115
  include Spectus
124
-
125
- it { "ルビー".valid_encoding? }.MUST be_true
126
- # => Spectus::Result::Pass(actual: true, error: nil, expected: nil, got: true, matcher: :be_true, negate: false, level: :MUST, valid: true)
127
116
  ```
128
117
 
129
- The result of the test shows that the spec passed.
118
+ All examples here assume that this has been done.
130
119
 
131
- ### Absolute Prohibition
120
+ ### Absolute Requirement
132
121
 
133
- Given the "`foo`" object, when it receives `length` method, then it **MUST NOT** raise the `NoMethodError` exception:
122
+ There's only one bat:
134
123
 
135
124
  ```ruby
136
- require "spectus"
125
+ it { "🦇".size }.MUST equal 1
126
+ # => Spectus::Result::Pass(actual: 1, error: nil, expected: 1, got: true, matcher: :equal, negate: false, level: :MUST)
127
+ ```
137
128
 
138
- include Spectus
129
+ ### Absolute Prohibition
139
130
 
140
- it { "foo".length }.MUST_NOT raise_exception NoMethodError
141
- # => Spectus::Result::Pass(actual: 3, error: nil, expected: NoMethodError, got: true, matcher: :raise_exception, negate: true, level: :MUST, valid: true)
142
- ```
131
+ The true from the false:
143
132
 
144
- The result of the test shows that the spec passed.
133
+ ```ruby
134
+ it { false }.MUST_NOT be_true
135
+ # => Spectus::Result::Pass(actual: false, error: nil, expected: nil, got: true, matcher: :be_true, negate: true, level: :MUST)
136
+ ```
145
137
 
146
138
  ### Recommended
147
139
 
148
- Given the `BasicObject` object, when it receives `superclass` method, then it **SHOULD** return the explicit blank class `NilClass`:
140
+ A well-known joke. An addition of `0.1` and `0.2` is deadly precise:
149
141
 
150
142
  ```ruby
151
- require "spectus"
152
-
153
- include Spectus
154
-
155
- it { BasicObject.superclass }.SHOULD equal NilClass
156
- # => Spectus::Result::Pass(actual: nil, error: nil, expected: NilClass, got: false, matcher: :equal, negate: false, level: :SHOULD, valid: false)
143
+ it { 0.1 + 0.2 }.SHOULD equal 0.3
144
+ # => Spectus::Result::Pass(actual: 0.30000000000000004, error: nil, expected: 0.3, got: false, matcher: :equal, negate: false, level: :SHOULD)
157
145
  ```
158
146
 
159
- Instead of the expected `NilClass` class, its sole instance (which is `nil`) was returned.
160
- However, because there isn't any exception, the result of the test shows that the spec passed.
161
-
162
147
  ### Not Recommended
163
148
 
164
- Given the "`1`" object, when it receives `+(1)` method, then it **SHOULD NOT** return the "`11`" value:
149
+ The situation should still be under control:
165
150
 
166
151
  ```ruby
167
- require "spectus"
168
-
169
- include Spectus
170
-
171
- it { "1" + 1 }.SHOULD_NOT eql "11"
172
- # raise Spectus::Result::Fail(actual: nil, error: #<TypeError: no implicit conversion of Integer into String>, expected: "11", got: nil, matcher: :eql, negate: true, level: :SHOULD, valid: false)
152
+ it { BOOM }.SHOULD_NOT raise_exception SystemExit
173
153
  ```
174
154
 
175
- There was a `TypeError` exception, the result of the test shows that the spec failed.
155
+ ```txt
156
+ Traceback (most recent call last):
157
+ 2: from ./bin/console:8:in `<main>'
158
+ 1: from (irb):8
159
+ Spectus::Result::Fail (NameError: uninitialized constant BOOM.)
160
+ ```
176
161
 
177
162
  ### Optional
178
163
 
179
- Given the "`foo`" object, when it receives `blank?` method, then it **MAY** be `false`:
164
+ An empty array is blank, right?
180
165
 
181
166
  ```ruby
182
- require "spectus"
183
-
184
- include Spectus
185
-
186
- it { "foo".blank? }.MAY be_false
187
- # => Spectus::Result::Pass(actual: nil, error: #<NoMethodError: undefined method `blank?' for "foo":String>, expected: nil, got: nil, matcher: :be_false, negate: false, level: :MAY, valid: false)
167
+ it { [].blank? }.MAY be_true
168
+ # => Spectus::Result::Pass(actual: nil, error: #<NoMethodError: undefined method `blank?' for []:Array>, expected: nil, got: nil, matcher: :be_true, negate: false, level: :MAY)
188
169
  ```
189
170
 
190
- The optional `blank?` method is not implemented (unlike in [Ruby on Rails](https://api.rubyonrails.org/classes/Object.html#method-i-blank-3F), for instance), so the result of the test shows that the spec passed.
171
+ Damn, I forgot to load activesupport. 🤦‍♂️
172
+
173
+ That said, the test is passing due to the _not-implemented-like_ raised exception: `NoMethodError`.
191
174
 
192
175
  ## Code Isolation
193
176
 
@@ -200,26 +183,22 @@ Because they may or may not be desired, each requirement level has 2 versions:
200
183
  Example of test without isolation:
201
184
 
202
185
  ```ruby
203
- require "spectus"
204
-
205
- include Spectus
206
-
207
186
  greeting = "Hello, world!"
187
+
208
188
  it { greeting.gsub!("world", "Alice") }.MUST eql "Hello, Alice!"
209
- # => Spectus::Result::Pass(actual: "Hello, Alice!", error: nil, expected: "Hello, Alice!", got: true, matcher: :eql, negate: false, level: :MUST, valid: true)
189
+ # => Spectus::Result::Pass(actual: "Hello, Alice!", error: nil, expected: "Hello, Alice!", got: true, matcher: :eql, negate: false, level: :MUST)
190
+
210
191
  greeting # => "Hello, Alice!"
211
192
  ```
212
193
 
213
194
  Example of test in isolation:
214
195
 
215
196
  ```ruby
216
- require "spectus"
217
-
218
- include Spectus
219
-
220
197
  greeting = "Hello, world!"
198
+
221
199
  it { greeting.gsub!("world", "Alice") }.MUST! eql "Hello, Alice!"
222
- # => Spectus::Result::Pass(actual: "Hello, Alice!", error: nil, expected: "Hello, Alice!", got: true, matcher: :eql, negate: false, level: :MUST, valid: true)
200
+ # => Spectus::Result::Pass(actual: "Hello, Alice!", error: nil, expected: "Hello, Alice!", got: true, matcher: :eql, negate: false, level: :MUST)
201
+
223
202
  greeting # => "Hello, world!"
224
203
  ```
225
204
 
data/lib/spectus.rb CHANGED
@@ -33,9 +33,9 @@ require_relative File.join("spectus", "expectation_target")
33
33
  # end
34
34
  #
35
35
  # t = Spec.new("foo")
36
- # t.test_a # => Spectus::Result::Pass(actual: "FOO", error: nil, expected: "FOO", got: true, matcher: :eql, negate: false, level: :MUST, valid: true)
37
- # t.test_b # => Spectus::Result::Pass(actual: nil, error: #<NoMethodError: undefined method `blank?' for "foo":String>, expected: nil, got: nil, matcher: :be_true, negate: false, level: :MAY, valid: false)
38
- # t.test_c # => Spectus::Result::Pass(actual: 3, error: nil, expected: 42, got: false, matcher: :equal, negate: false, level: :SHOULD, valid: false)
36
+ # t.test_a # => Spectus::Result::Pass(actual: "FOO", error: nil, expected: "FOO", got: true, matcher: :eql, negate: false, level: :MUST)
37
+ # t.test_b # => Spectus::Result::Pass(actual: nil, error: #<NoMethodError: undefined method `blank?' for "foo":String>, expected: nil, got: nil, matcher: :be_true, negate: false, level: :MAY)
38
+ # t.test_c # => Spectus::Result::Pass(actual: 3, error: nil, expected: 42, got: false, matcher: :equal, negate: false, level: :SHOULD)
39
39
  #
40
40
  # Or even directly used like this.
41
41
  #
@@ -44,7 +44,7 @@ require_relative File.join("spectus", "expectation_target")
44
44
  #
45
45
  # include Spectus
46
46
  #
47
- # it { 42 }.MUST equal 42 # => #<Spectus::Result::Pass...>
47
+ # it { 42 }.MUST equal 42 # => Spectus::Result::Pass(actual: 42, error: nil, expected: 42, got: true, matcher: :equal, negate: false, level: :MUST
48
48
  #
49
49
  # It also includes a collection of expectation matchers 🤹
50
50
  #
@@ -103,7 +103,7 @@ module Spectus
103
103
  # Expectations are built with this method.
104
104
  #
105
105
  # @example An _absolute requirement_ definition.
106
- # it { 42 }.MUST equal 42 # => #<Spectus::Result::Pass...>
106
+ # it { 42 }.MUST equal 42 # => Spectus::Result::Pass(actual: 42, error: nil, expected: 42, got: true, matcher: :equal, negate: false, level: :MUST
107
107
  #
108
108
  # @param input [Proc] The code to test.
109
109
  #
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative File.join("..", "exam")
3
+ require "test_tube"
4
+
4
5
  require_relative File.join("..", "result")
5
6
 
6
7
  module Spectus
@@ -12,22 +13,21 @@ module Spectus
12
13
  #
13
14
  # @param callable [#call] The callable object to test.
14
15
  # @param isolation [Boolean] Compute actual in isolation?
15
- # @param negate [Boolean] Positive or negative assertion?
16
+ # @param negate [Boolean] Invert the matcher or not.
16
17
  # @param matcher [#matches?] The matcher.
17
- def initialize(callable:, isolation:, negate:, matcher:)
18
- @negate = negate
19
- @matcher = matcher
20
-
21
- @exam = Exam.new(
22
- callable: callable,
18
+ def initialize(callable:, isolation:, matcher:, negate:)
19
+ @negate = negate
20
+ @matcher = matcher
21
+ @experiment = ::TestTube.invoke(
22
+ callable,
23
23
  isolation: isolation,
24
- negate: negate,
25
- matcher: matcher
24
+ matcher: matcher,
25
+ negate: negate
26
26
  )
27
27
  end
28
28
 
29
- # @return [#Exam] The exam.
30
- attr_reader :exam
29
+ # @return [TestTube::Base] The experiment.
30
+ attr_reader :experiment
31
31
 
32
32
  # @return [#matches?] The matcher that performed a boolean comparison
33
33
  # between the actual value and the expected value.
@@ -39,20 +39,21 @@ module Spectus
39
39
  # @return [Spectus::Result::Pass] The expectation passed.
40
40
  def call
41
41
  Result.call(pass?).with(
42
- actual: exam.actual,
43
- error: exam.exception,
42
+ actual: experiment.actual,
43
+ error: experiment.error,
44
44
  expected: matcher.expected,
45
- got: exam.got,
46
- negate: negate?,
47
- valid: exam.valid?,
45
+ got: experiment.got,
46
+ level: level,
48
47
  matcher: matcher.class.to_sym,
49
- level: level
48
+ negate: negate?
50
49
  )
51
50
  end
52
51
 
53
52
  protected
54
53
 
55
- # @return [Symbol] The requirement level.
54
+ # Some key words for use in RFCs to indicate requirement levels.
55
+ #
56
+ # @return [:MUST, :SHOULD, :MAY] The requirement level.
56
57
  def level
57
58
  self.class.name.split("::").fetch(-1).upcase.to_sym
58
59
  end
@@ -60,7 +61,7 @@ module Spectus
60
61
  # @note The boolean comparison between the actual value and the expected
61
62
  # value can be evaluated to a negative assertion.
62
63
  #
63
- # @return [Boolean] Positive or negative assertion?
64
+ # @return [Boolean] Invert the matcher or not.
64
65
  def negate?
65
66
  @negate
66
67
  end
@@ -10,7 +10,7 @@ module Spectus
10
10
  #
11
11
  # @return [Boolean] Report if the low expectation pass or fail?
12
12
  def pass?
13
- super || exam.exception.is_a?(::NoMethodError)
13
+ super || experiment.error.is_a?(::NoMethodError)
14
14
  end
15
15
  end
16
16
  end
@@ -10,7 +10,7 @@ module Spectus
10
10
  #
11
11
  # @return [Boolean] Report if the high expectation pass or fail?
12
12
  def pass?
13
- exam.valid?
13
+ experiment.got.equal?(true)
14
14
  end
15
15
  end
16
16
  end
@@ -10,7 +10,7 @@ module Spectus
10
10
  #
11
11
  # @return [Boolean] Report if the medium expectation pass or fail?
12
12
  def pass?
13
- super || exam.exception.nil?
13
+ super || experiment.error.nil?
14
14
  end
15
15
  end
16
16
  end
metadata CHANGED
@@ -1,57 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spectus
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.4
4
+ version: 3.4.0
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-16 00:00:00.000000000 Z
11
+ date: 2021-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: defi
14
+ name: expresenter
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.5
19
+ version: 1.3.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.5
26
+ version: 1.3.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: expresenter
28
+ name: matchi
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.2.1
33
+ version: 2.1.0
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.2.1
40
+ version: 2.1.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: matchi
42
+ name: test_tube
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 2.1.0
47
+ version: 1.0.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 2.1.0
54
+ version: 1.0.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: brutal
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -187,7 +187,6 @@ files:
187
187
  - LICENSE.md
188
188
  - README.md
189
189
  - lib/spectus.rb
190
- - lib/spectus/exam.rb
191
190
  - lib/spectus/expectation_target.rb
192
191
  - lib/spectus/requirement_level/base.rb
193
192
  - lib/spectus/requirement_level/may.rb
data/lib/spectus/exam.rb DELETED
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "defi"
4
-
5
- module Spectus
6
- # This class evaluate the expectation with the passed block.
7
- class Exam
8
- # Execute the untested code from the passed block against the matcher.
9
- #
10
- # rubocop:disable Lint/RescueException
11
- #
12
- # @param callable [#call] The callable object to test.
13
- # @param isolation [Boolean] Compute actual in isolation?
14
- # @param negate [Boolean] Positive or negative assertion?
15
- # @param matcher [#matches?] The matcher.
16
- def initialize(callable:, isolation:, negate:, matcher:)
17
- @got = negate ^ matcher.matches? do
18
- value = if isolation
19
- send_call.to!(callable)
20
- else
21
- send_call.to(callable)
22
- end
23
-
24
- @actual = value.object
25
-
26
- value.call
27
- end
28
- rescue ::Exception => e
29
- @actual = nil
30
- @exception = e
31
- end
32
- # rubocop:enable Lint/RescueException
33
-
34
- # @return [#object_id] The actual value.
35
- attr_reader :actual
36
-
37
- # @return [Exception, nil] An exception.
38
- attr_reader :exception
39
-
40
- # @return [Boolean, nil] Report to the spec requirement level if the
41
- # expectation is true or false.
42
- attr_reader :got
43
-
44
- # @return [Defi::Challenge] The challenge for the callable object.
45
- def send_call
46
- ::Defi.send(:call)
47
- end
48
-
49
- # Report to the spec requirement level if the test pass or fail.
50
- #
51
- # @return [Boolean] Report if the test pass or fail?
52
- def valid?
53
- exception.nil? ? got : false
54
- end
55
- end
56
- end