spectus 2.0.3 → 2.1.0

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