spectus 2.0.3 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|