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.
@@ -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
Binary file
data.tar.gz.sig CHANGED
Binary file
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /.bundle/
2
+ /.ruby-version
2
3
  /.yardoc
3
4
  /Gemfile.lock
4
5
  /_yardoc/
@@ -0,0 +1,12 @@
1
+ TrivialAccessors:
2
+ Enabled: true
3
+ ExactNameMatch: true
4
+
5
+ Style/MethodName:
6
+ Enabled: false
7
+
8
+ ParameterLists:
9
+ Max: 11
10
+
11
+ Metrics/MethodLength:
12
+ Max: 16
data/README.md CHANGED
@@ -36,7 +36,9 @@ Or install it yourself as:
36
36
 
37
37
  $ gem install spectus
38
38
 
39
- ## Results
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
- **Absolute requirement** definition:
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 { 'foo'.upcase }.MUST eql: 'FOO' # => true
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
- **Absolute prohibition** definition:
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 equal: 42 # => true
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
- **Recommended** definition:
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 { 'foo'.valid_encoding? }.SHOULD equal: true # => true
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
- **Not recommended** definition:
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 { ''.blank? }.SHOULD_NOT raise_exception: NoMethodError # => true
99
+ Spectus.this { '1' + 1 }.SHOULD_NOT Eql: '11'
100
+ # => raise #<Spectus::Result::Fail: failing spec> exception
72
101
  ```
73
102
 
74
- **Optional** definition:
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'.bar }.MAY match: /^foo$/ # => true
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
@@ -2,7 +2,6 @@ require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new do |t|
5
- t.pattern = File.join 'test', '**', 'test_*.rb'
6
5
  t.verbose = true
7
6
  t.warning = true
8
7
  end
@@ -1 +1 @@
1
- 2.0.3
1
+ 2.1.0
@@ -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 equal: 42 # => true
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 equal: 42 # => true
15
+ # this { 42 }.MUST Equal: 42 # => #<Spectus::Result::Pass...>
16
16
  #
17
- # @yieldparam input the code to test.
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 'requirement_level', 'high'
2
- require_relative File.join 'requirement_level', 'medium'
3
- require_relative File.join 'requirement_level', 'low'
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 { stuff } # => ExpectationTarget wrapping the block
10
+ # this { actual value } # => ExpectationTarget wrapping the block
12
11
  class ExpectationTarget < BasicObject
13
12
  # Create a new expection target
14
13
  #
15
- # @yieldparam actual the value which is compared with the expected value.
16
- def initialize(&actual)
17
- @actual = actual
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 eql: 'FOO' # => true
30
+ # this { 'foo'.upcase }.MUST Eql: 'FOO'
27
31
  #
28
- # @param [Hash] definition
32
+ # @param definition [Array, Hash, Symbol]
29
33
  #
30
- # @return [Boolean] report if the expectation is true or false.
34
+ # @return [Result::Fail, Result::Pass] report if the spec pass or fail.
31
35
  def MUST(definition)
32
- RequirementLevel::High.new(definition).pass?(&@actual)
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'.length }.MUST_NOT equal: 42 # => true
43
+ # this { 'foo'.size }.MUST_NOT Equal: 42
42
44
  #
43
- # @param [Hash] definition
45
+ # @param definition [Array, Hash, Symbol]
44
46
  #
45
- # @return [Boolean] report if the expectation is true or false.
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).pass?(&@actual)
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 equal: true # => true
58
+ # this { 'foo'.valid_encoding? }.SHOULD Equal: true
59
59
  #
60
- # @param [Hash] definition
60
+ # @param definition [Array, Hash, Symbol]
61
61
  #
62
- # @return [Boolean] report if the expectation is true or false.
62
+ # @return [Result::Fail, Result::Pass] report if the spec pass or fail.
63
63
  def SHOULD(definition)
64
- RequirementLevel::Medium.new(definition).pass?(&@actual)
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 raise_exception: NoMethodError # => true
74
+ # this { ''.blank? }.SHOULD_NOT RaiseException: NoMethodError
77
75
  #
78
- # @param [Hash] definition
76
+ # @param definition [Array, Hash, Symbol]
79
77
  #
80
- # @return [Boolean] report if the expectation is true or false.
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).pass?(&@actual)
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 match: /^foo$/ # => true
96
+ # this { 'foo'.bar }.MAY Match: /^foo$/
101
97
  #
102
- # @param [Hash] definition
98
+ # @param definition [Array, Hash, Symbol]
103
99
  #
104
- # @return [Boolean] report if the expectation is true or false.
100
+ # @return [Result::Fail, Result::Pass] report if the spec pass or fail.
105
101
  def MAY(definition)
106
- RequirementLevel::Low.new(definition).pass?(&@actual)
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