spectus 2.0.3 → 2.1.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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +12 -0
- data/README.md +51 -11
- data/Rakefile +0 -1
- data/VERSION.semver +1 -1
- data/lib/spectus.rb +3 -3
- data/lib/spectus/challenge.rb +22 -0
- data/lib/spectus/expectation_target.rb +35 -39
- data/lib/spectus/level/base.rb +72 -0
- data/lib/spectus/level/high.rb +25 -0
- data/lib/spectus/level/low.rb +25 -0
- data/lib/spectus/level/medium.rb +25 -0
- data/lib/spectus/result/base.rb +83 -0
- data/lib/spectus/result/fail.rb +28 -0
- data/lib/spectus/result/pass.rb +30 -0
- data/lib/spectus/sandbox.rb +24 -8
- data/spectus.gemspec +10 -10
- data/test/support/coverage.rb +3 -0
- data/test/test_high_fail.rb +140 -0
- data/test/test_high_pass.rb +44 -0
- data/test/test_low_fail.rb +51 -0
- data/test/test_low_pass.rb +47 -0
- data/test/test_medium_fail.rb +50 -0
- data/test/test_medium_pass.rb +82 -0
- metadata +72 -89
- metadata.gz.sig +0 -0
- data/lib/spectus/matcher.rb +0 -20
- data/lib/spectus/requirement.rb +0 -29
- data/lib/spectus/requirement_level/high.rb +0 -16
- data/lib/spectus/requirement_level/low.rb +0 -22
- data/lib/spectus/requirement_level/medium.rb +0 -22
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4e7aff59bbeafa35f8d48f0ad922c18b67b3ab67
|
4
|
+
data.tar.gz: 4fc80ff8c310a2f088182976c0c1a770291c375e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d7bad2346b48569a149e01e486990b64f62a999b2a98f4f5530868f6ad43f835e2b6c87d77f30dc922e53f476996926e1a7cadfde8a44d06c303ff9d2ab461a0
|
7
|
+
data.tar.gz: 96073c63f2c891acf9a44e46fc36b1fea47204c288699712a4205480ee80f264c449eef926cf30a76670896ec896f055b1dd0fd83affb133e56c0b076df10443
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
data/README.md
CHANGED
@@ -36,7 +36,9 @@ Or install it yourself as:
|
|
36
36
|
|
37
37
|
$ gem install spectus
|
38
38
|
|
39
|
-
##
|
39
|
+
## Expectation
|
40
|
+
|
41
|
+
An expectation is an assertion that is either `true` or `false`.
|
40
42
|
|
41
43
|
| Requirement levels | **MUST** | **SHOULD** | **MAY** |
|
42
44
|
| ------------------------- | -------- | ---------- | ------- |
|
@@ -45,38 +47,72 @@ Or install it yourself as:
|
|
45
47
|
| Implemented & Exception | `false` | `false` | `false` |
|
46
48
|
| Not implemented | `false` | `false` | `true` |
|
47
49
|
|
50
|
+
## Results
|
51
|
+
|
52
|
+
There are two cases:
|
53
|
+
|
54
|
+
* when an expectation is `true`, an instance of `Spectus::Result::Pass` is returned;
|
55
|
+
* when an expectation is `false`, an instance of `Spectus::Result::Fail` is raised.
|
56
|
+
|
57
|
+
Both instances share the same interface.
|
58
|
+
|
48
59
|
## Usage
|
49
60
|
|
50
|
-
|
61
|
+
### Absolute requirement
|
62
|
+
|
63
|
+
Given the `"ルビー"` object, when it receives `valid_encoding?` method, then it **MUST** be `true`:
|
51
64
|
|
52
65
|
```ruby
|
53
|
-
Spectus.this { '
|
66
|
+
Spectus.this { 'ルビー'.valid_encoding? }.MUST :BeTrue
|
67
|
+
# => #<Spectus::Result::Pass:0x007fa41c371b90 @message="passing spec", @subject=#<Proc:0x007fa41c3721f8@(irb):1>, @challenge=:call, @context=[], @actual=true, @expected=:BeTrue, @got=true, @error=nil, @level=:High, @negate=false, @valid=true>
|
54
68
|
```
|
55
69
|
|
56
|
-
|
70
|
+
The result of the test shows that the spec passed.
|
71
|
+
|
72
|
+
### Absolute prohibition
|
73
|
+
|
74
|
+
Given the `"foo"` object, when it receives `length` method, then it **MUST NOT** raise the `NoMethodError` exception:
|
57
75
|
|
58
76
|
```ruby
|
59
|
-
Spectus.this { 'foo'.length }.MUST_NOT
|
77
|
+
Spectus.this { 'foo'.length }.MUST_NOT RaiseException: NoMethodError
|
78
|
+
# => #<Spectus::Result::Pass:0x007fa41e001d50 @message="passing spec", @subject=#<Proc:0x007fa41c34a928@(irb):2>, @challenge=:call, @context=[], @actual=3, @expected={:RaiseException=>NoMethodError}, @got=true, @error=nil, @level=:High, @negate=true, @valid=true>
|
60
79
|
```
|
61
80
|
|
62
|
-
|
81
|
+
The result of the test shows that the spec passed.
|
82
|
+
|
83
|
+
### Recommended
|
84
|
+
|
85
|
+
Given the `BasicObject` object, when it receives `superclass` method, then it **SHOULD** return the explicit blank class `NilClass`:
|
63
86
|
|
64
87
|
```ruby
|
65
|
-
Spectus.this {
|
88
|
+
Spectus.this { BasicObject.superclass }.SHOULD Equal: NilClass
|
89
|
+
# => #<Spectus::Result::Pass:0x007fa41c2f1d00 @message="passing spec", @subject=#<Proc:0x007fa41c2f2110@(irb):3>, @challenge=:call, @context=[], @actual=nil, @expected={:Equal=>NilClass}, @got=false, @error=nil, @level=:Medium, @negate=false, @valid=false>
|
66
90
|
```
|
67
91
|
|
68
|
-
|
92
|
+
Even if the sole instance of `NilClass` (`nil`) was returned, there isn't any exception so the result of the test shows that the spec passed.
|
93
|
+
|
94
|
+
### Not recommended
|
95
|
+
|
96
|
+
Given the `"1"` object, when it receives `+(1)` method, then it **SHOULD NOT** return the `"11"` value:
|
69
97
|
|
70
98
|
```ruby
|
71
|
-
Spectus.this { ''
|
99
|
+
Spectus.this { '1' + 1 }.SHOULD_NOT Eql: '11'
|
100
|
+
# => raise #<Spectus::Result::Fail: failing spec> exception
|
72
101
|
```
|
73
102
|
|
74
|
-
|
103
|
+
There was a `TypeError` exception, the result of the test shows that the spec failed.
|
104
|
+
|
105
|
+
### Optional
|
106
|
+
|
107
|
+
Given the `"foo"` object, when it receives `bar` method, then it **MAY** match the regular expression `/^foo$/`:
|
75
108
|
|
76
109
|
```ruby
|
77
|
-
Spectus.this { 'foo'.
|
110
|
+
Spectus.this { 'foo'.blank? }.MAY :BeFalse
|
111
|
+
# => #<Spectus::Result::Pass:0x007fa41c2bae18 @message="passing spec", @subject=#<Proc:0x007fa41c2bb368@(irb):6>, @challenge=:call, @context=[], @actual=nil, @expected=:BeFalse, @got=nil, @error=#<NoMethodError: undefined method `blank?' for "foo":String>, @level=:Low, @negate=false, @valid=false>
|
78
112
|
```
|
79
113
|
|
114
|
+
The optional `blank?` method is not implemented (unlike in [Ruby on Rails](http://api.rubyonrails.org/classes/Object.html#method-i-blank-3F) for instance), so the result of the test shows that the spec passed.
|
115
|
+
|
80
116
|
## Versioning
|
81
117
|
|
82
118
|
__Spectus__ follows [Semantic Versioning 2.0](http://semver.org/).
|
@@ -88,3 +124,7 @@ __Spectus__ follows [Semantic Versioning 2.0](http://semver.org/).
|
|
88
124
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
89
125
|
4. Push to the branch (`git push origin my-new-feature`)
|
90
126
|
5. Create a new Pull Request
|
127
|
+
|
128
|
+
## License
|
129
|
+
|
130
|
+
See `LICENSE.md` file.
|
data/Rakefile
CHANGED
data/VERSION.semver
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0
|
1
|
+
2.1.0
|
data/lib/spectus.rb
CHANGED
@@ -5,16 +5,16 @@ require_relative File.join 'spectus', 'expectation_target'
|
|
5
5
|
# @api public
|
6
6
|
#
|
7
7
|
# @example 42 MUST be equal to 42
|
8
|
-
# Spectus.this { 42 }.MUST
|
8
|
+
# Spectus.this { 42 }.MUST Equal: 42 # => #<Spectus::Result::Pass...>
|
9
9
|
module Spectus
|
10
10
|
# Expectations are built with this method.
|
11
11
|
#
|
12
12
|
# @api public
|
13
13
|
#
|
14
14
|
# @example Absolute requirement definition
|
15
|
-
# this { 42 }.MUST
|
15
|
+
# this { 42 }.MUST Equal: 42 # => #<Spectus::Result::Pass...>
|
16
16
|
#
|
17
|
-
# @
|
17
|
+
# @param input [Proc] the code to test.
|
18
18
|
#
|
19
19
|
# @return [ExpectationTarget] the expectation target.
|
20
20
|
def self.this(&input)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Spectus
|
2
|
+
# This class evaluate the expectation with the passed block.
|
3
|
+
#
|
4
|
+
# @api private
|
5
|
+
#
|
6
|
+
class Challenge
|
7
|
+
# A Query to the subject.
|
8
|
+
#
|
9
|
+
# @param [#to_sym] method_id the identifier of a method.
|
10
|
+
# @param [Array] args the arguments of the method.
|
11
|
+
def initialize(method_id, *args)
|
12
|
+
@symbol = method_id.to_sym
|
13
|
+
@args = args
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Symbol] the method to call on the subject.
|
17
|
+
attr_reader :symbol
|
18
|
+
|
19
|
+
# @return [Array] the parameters following the method.
|
20
|
+
attr_reader :args
|
21
|
+
end
|
22
|
+
end
|
@@ -1,50 +1,52 @@
|
|
1
|
-
require_relative File.join '
|
2
|
-
require_relative File.join '
|
3
|
-
require_relative File.join '
|
1
|
+
require_relative File.join 'level', 'high'
|
2
|
+
require_relative File.join 'level', 'medium'
|
3
|
+
require_relative File.join 'level', 'low'
|
4
|
+
require_relative 'challenge'
|
4
5
|
|
5
6
|
module Spectus
|
6
7
|
# Wraps the target of an expectation.
|
7
8
|
#
|
8
|
-
# @api private
|
9
|
-
#
|
10
9
|
# @example
|
11
|
-
# this {
|
10
|
+
# this { actual value } # => ExpectationTarget wrapping the block
|
12
11
|
class ExpectationTarget < BasicObject
|
13
12
|
# Create a new expection target
|
14
13
|
#
|
15
|
-
# @
|
16
|
-
def initialize(&
|
17
|
-
@
|
14
|
+
# @param subject [Proc] the value which is compared with the expected value.
|
15
|
+
def initialize(&subject)
|
16
|
+
@subject = subject
|
17
|
+
@challenge = Challenge.new(:call)
|
18
18
|
end
|
19
19
|
|
20
|
+
# @return [BasicObject] the front object to be tested.
|
21
|
+
attr_reader :subject
|
22
|
+
|
23
|
+
# @return [Challenge] the challenge to call on the subject.
|
24
|
+
attr_reader :challenge
|
25
|
+
|
20
26
|
# This word, or the terms "REQUIRED" or "SHALL", mean that the
|
21
27
|
# definition is an absolute requirement of the specification.
|
22
28
|
#
|
23
|
-
# @api public
|
24
|
-
#
|
25
29
|
# @example _Absolute requirement_ definition
|
26
|
-
# this { 'foo'.upcase }.MUST
|
30
|
+
# this { 'foo'.upcase }.MUST Eql: 'FOO'
|
27
31
|
#
|
28
|
-
# @param [Hash]
|
32
|
+
# @param definition [Array, Hash, Symbol]
|
29
33
|
#
|
30
|
-
# @return [
|
34
|
+
# @return [Result::Fail, Result::Pass] report if the spec pass or fail.
|
31
35
|
def MUST(definition)
|
32
|
-
RequirementLevel::High.new(definition).
|
36
|
+
RequirementLevel::High.new(definition, false, subject, challenge).result
|
33
37
|
end
|
34
38
|
|
35
39
|
# This phrase, or the phrase "SHALL NOT", mean that the
|
36
40
|
# definition is an absolute prohibition of the specification.
|
37
41
|
#
|
38
|
-
# @api public
|
39
|
-
#
|
40
42
|
# @example _Absolute prohibition_ definition
|
41
|
-
# this { 'foo'.
|
43
|
+
# this { 'foo'.size }.MUST_NOT Equal: 42
|
42
44
|
#
|
43
|
-
# @param [Hash]
|
45
|
+
# @param definition [Array, Hash, Symbol]
|
44
46
|
#
|
45
|
-
# @return [
|
47
|
+
# @return [Result::Fail, Result::Pass] report if the spec pass or fail.
|
46
48
|
def MUST_NOT(definition)
|
47
|
-
RequirementLevel::High.new(definition, true).
|
49
|
+
RequirementLevel::High.new(definition, true, subject, challenge).result
|
48
50
|
end
|
49
51
|
|
50
52
|
# This word, or the adjective "RECOMMENDED", mean that there
|
@@ -52,16 +54,14 @@ module Spectus
|
|
52
54
|
# particular item, but the full implications must be understood and
|
53
55
|
# carefully weighed before choosing a different course.
|
54
56
|
#
|
55
|
-
# @api public
|
56
|
-
#
|
57
57
|
# @example _Recommended_ definition
|
58
|
-
# this { 'foo'.valid_encoding? }.SHOULD
|
58
|
+
# this { 'foo'.valid_encoding? }.SHOULD Equal: true
|
59
59
|
#
|
60
|
-
# @param [Hash]
|
60
|
+
# @param definition [Array, Hash, Symbol]
|
61
61
|
#
|
62
|
-
# @return [
|
62
|
+
# @return [Result::Fail, Result::Pass] report if the spec pass or fail.
|
63
63
|
def SHOULD(definition)
|
64
|
-
RequirementLevel::Medium.new(definition).
|
64
|
+
RequirementLevel::Medium.new(definition, false, subject, challenge).result
|
65
65
|
end
|
66
66
|
|
67
67
|
# This phrase, or the phrase "NOT RECOMMENDED" mean that
|
@@ -70,16 +70,14 @@ module Spectus
|
|
70
70
|
# implications should be understood and the case carefully weighed
|
71
71
|
# before implementing any behavior described with this label.
|
72
72
|
#
|
73
|
-
# @api public
|
74
|
-
#
|
75
73
|
# @example _Not recommended_ definition
|
76
|
-
# this { ''.blank? }.SHOULD_NOT
|
74
|
+
# this { ''.blank? }.SHOULD_NOT RaiseException: NoMethodError
|
77
75
|
#
|
78
|
-
# @param [Hash]
|
76
|
+
# @param definition [Array, Hash, Symbol]
|
79
77
|
#
|
80
|
-
# @return [
|
78
|
+
# @return [Result::Fail, Result::Pass] report if the spec pass or fail.
|
81
79
|
def SHOULD_NOT(definition)
|
82
|
-
RequirementLevel::Medium.new(definition, true).
|
80
|
+
RequirementLevel::Medium.new(definition, true, subject, challenge).result
|
83
81
|
end
|
84
82
|
|
85
83
|
# This word, or the adjective "OPTIONAL", mean that an item is
|
@@ -94,16 +92,14 @@ module Spectus
|
|
94
92
|
# does not include the option (except, of course, for the feature the
|
95
93
|
# option provides.)
|
96
94
|
#
|
97
|
-
# @api public
|
98
|
-
#
|
99
95
|
# @example _Optional_ definition
|
100
|
-
# this { 'foo'.bar }.MAY
|
96
|
+
# this { 'foo'.bar }.MAY Match: /^foo$/
|
101
97
|
#
|
102
|
-
# @param [Hash]
|
98
|
+
# @param definition [Array, Hash, Symbol]
|
103
99
|
#
|
104
|
-
# @return [
|
100
|
+
# @return [Result::Fail, Result::Pass] report if the spec pass or fail.
|
105
101
|
def MAY(definition)
|
106
|
-
RequirementLevel::Low.new(definition).
|
102
|
+
RequirementLevel::Low.new(definition, false, subject, challenge).result
|
107
103
|
end
|
108
104
|
end
|
109
105
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require_relative File.join '..', 'sandbox'
|
2
|
+
require_relative File.join '..', 'result', 'fail'
|
3
|
+
require_relative File.join '..', 'result', 'pass'
|
4
|
+
|
5
|
+
module Spectus
|
6
|
+
module RequirementLevel
|
7
|
+
# Requirement level's base class.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
#
|
11
|
+
class Base
|
12
|
+
# Initialize the requirement level class.
|
13
|
+
#
|
14
|
+
# @param definition [Array, Hash, Symbol] The definition of the expected
|
15
|
+
# value.
|
16
|
+
# @param negate [Boolean] Evaluate to a negative assertion.
|
17
|
+
# @param subject [#object_id] the front object to test.
|
18
|
+
# @param challenge [Challenge] a challenge for the subject.
|
19
|
+
def initialize(definition, negate, subject, challenge)
|
20
|
+
@definition = definition
|
21
|
+
@negate = negate
|
22
|
+
@subject = subject
|
23
|
+
@challenge = challenge
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
# @param state [Sandbox] The sandbox that tested the code.
|
29
|
+
#
|
30
|
+
# @return [Result::Pass] pass the spec.
|
31
|
+
def pass!(state)
|
32
|
+
Result::Pass.new('passing spec', *result_signature(state))
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param state [Sandbox] The sandbox that tested the code.
|
36
|
+
#
|
37
|
+
# @raise [Result::Fail] fail the spec.
|
38
|
+
def fail!(state)
|
39
|
+
fail(Result::Fail.new('failing spec', *result_signature(state)))
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param state [Sandbox] The sandbox that tested the code.
|
43
|
+
#
|
44
|
+
# @return [Array] list of parameters.
|
45
|
+
def result_signature(state)
|
46
|
+
[
|
47
|
+
@subject,
|
48
|
+
@challenge.symbol,
|
49
|
+
@challenge.args,
|
50
|
+
state.actual,
|
51
|
+
@definition,
|
52
|
+
state.got,
|
53
|
+
state.exception,
|
54
|
+
level,
|
55
|
+
@negate,
|
56
|
+
state.valid?
|
57
|
+
]
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Symbol] the requirement level.
|
61
|
+
def level
|
62
|
+
self.class.name.split('::').last.to_sym
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Sandbox] the sandbox.
|
66
|
+
def sandbox
|
67
|
+
Sandbox.new(@definition, @negate, @subject, @challenge.symbol,
|
68
|
+
*@challenge.args)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module Spectus
|
4
|
+
module RequirementLevel
|
5
|
+
# High requirement level's class.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
#
|
9
|
+
class High < Base
|
10
|
+
# Evaluate the expectation.
|
11
|
+
#
|
12
|
+
# @return [Result::Fail, Result::Pass] report if the high expectation
|
13
|
+
# pass or fail.
|
14
|
+
def result
|
15
|
+
state = sandbox
|
16
|
+
|
17
|
+
if state.valid?
|
18
|
+
pass!(state)
|
19
|
+
else
|
20
|
+
fail!(state)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module Spectus
|
4
|
+
module RequirementLevel
|
5
|
+
# Low requirement level's class.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
#
|
9
|
+
class Low < Base
|
10
|
+
# Evaluate the expectation.
|
11
|
+
#
|
12
|
+
# @return [Result::Fail, Result::Pass] report if the low expectation
|
13
|
+
# pass or fail.
|
14
|
+
def result
|
15
|
+
state = sandbox
|
16
|
+
|
17
|
+
if state.valid? || state.exception.class.equal?(::NoMethodError)
|
18
|
+
pass!(state)
|
19
|
+
else
|
20
|
+
fail!(state)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|