rspec-expectations 3.0.0.beta1 → 3.0.0.beta2

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.
Files changed (122) hide show
  1. data.tar.gz.sig +2 -2
  2. data/.yardopts +1 -0
  3. data/Changelog.md +138 -0
  4. data/README.md +75 -8
  5. data/features/README.md +2 -2
  6. data/features/built_in_matchers/README.md +12 -9
  7. data/features/built_in_matchers/comparisons.feature +2 -2
  8. data/features/built_in_matchers/contain_exactly.feature +46 -0
  9. data/features/built_in_matchers/expect_change.feature +2 -2
  10. data/features/built_in_matchers/include.feature +0 -48
  11. data/features/built_in_matchers/output.feature +70 -0
  12. data/features/composing_matchers.feature +250 -0
  13. data/features/compound_expectations.feature +45 -0
  14. data/features/custom_matchers/access_running_example.feature +1 -1
  15. data/features/custom_matchers/define_matcher.feature +6 -6
  16. data/features/custom_matchers/define_matcher_outside_rspec.feature +4 -8
  17. data/features/test_frameworks/{test_unit.feature → minitest.feature} +11 -11
  18. data/lib/rspec/expectations.rb +31 -42
  19. data/lib/rspec/expectations/diff_presenter.rb +141 -0
  20. data/lib/rspec/expectations/differ.rb +22 -132
  21. data/lib/rspec/expectations/encoded_string.rb +56 -0
  22. data/lib/rspec/expectations/expectation_target.rb +0 -30
  23. data/lib/rspec/expectations/fail_with.rb +2 -2
  24. data/lib/rspec/expectations/handler.rb +128 -31
  25. data/lib/rspec/expectations/minitest_integration.rb +16 -0
  26. data/lib/rspec/expectations/syntax.rb +4 -58
  27. data/lib/rspec/expectations/version.rb +1 -1
  28. data/lib/rspec/matchers.rb +298 -60
  29. data/lib/rspec/matchers/aliased_matcher.rb +35 -0
  30. data/lib/rspec/matchers/built_in.rb +37 -33
  31. data/lib/rspec/matchers/built_in/base_matcher.rb +25 -15
  32. data/lib/rspec/matchers/built_in/be.rb +23 -31
  33. data/lib/rspec/matchers/built_in/be_between.rb +55 -0
  34. data/lib/rspec/matchers/built_in/be_within.rb +15 -11
  35. data/lib/rspec/matchers/built_in/change.rb +198 -81
  36. data/lib/rspec/matchers/built_in/compound.rb +106 -0
  37. data/lib/rspec/matchers/built_in/contain_exactly.rb +245 -0
  38. data/lib/rspec/matchers/built_in/eq.rb +43 -4
  39. data/lib/rspec/matchers/built_in/eql.rb +2 -2
  40. data/lib/rspec/matchers/built_in/equal.rb +35 -18
  41. data/lib/rspec/matchers/built_in/has.rb +16 -15
  42. data/lib/rspec/matchers/built_in/include.rb +45 -23
  43. data/lib/rspec/matchers/built_in/match.rb +6 -3
  44. data/lib/rspec/matchers/built_in/operators.rb +103 -0
  45. data/lib/rspec/matchers/built_in/output.rb +108 -0
  46. data/lib/rspec/matchers/built_in/raise_error.rb +9 -15
  47. data/lib/rspec/matchers/built_in/respond_to.rb +5 -4
  48. data/lib/rspec/matchers/built_in/satisfy.rb +4 -3
  49. data/lib/rspec/matchers/built_in/start_and_end_with.rb +37 -16
  50. data/lib/rspec/matchers/built_in/throw_symbol.rb +6 -5
  51. data/lib/rspec/matchers/built_in/yield.rb +31 -29
  52. data/lib/rspec/matchers/composable.rb +138 -0
  53. data/lib/rspec/matchers/dsl.rb +330 -0
  54. data/lib/rspec/matchers/generated_descriptions.rb +6 -6
  55. data/lib/rspec/matchers/matcher_delegator.rb +33 -0
  56. data/lib/rspec/matchers/pretty.rb +13 -2
  57. data/spec/rspec/expectations/{differ_spec.rb → diff_presenter_spec.rb} +56 -36
  58. data/spec/rspec/expectations/encoded_string_spec.rb +74 -0
  59. data/spec/rspec/expectations/extensions/kernel_spec.rb +11 -11
  60. data/spec/rspec/expectations/fail_with_spec.rb +8 -8
  61. data/spec/rspec/expectations/handler_spec.rb +27 -49
  62. data/spec/rspec/expectations/minitest_integration_spec.rb +27 -0
  63. data/spec/rspec/expectations/syntax_spec.rb +17 -67
  64. data/spec/rspec/expectations_spec.rb +7 -52
  65. data/spec/rspec/matchers/aliased_matcher_spec.rb +48 -0
  66. data/spec/rspec/matchers/aliases_spec.rb +449 -0
  67. data/spec/rspec/matchers/{base_matcher_spec.rb → built_in/base_matcher_spec.rb} +24 -3
  68. data/spec/rspec/matchers/built_in/be_between_spec.rb +159 -0
  69. data/spec/rspec/matchers/{be_instance_of_spec.rb → built_in/be_instance_of_spec.rb} +0 -0
  70. data/spec/rspec/matchers/{be_kind_of_spec.rb → built_in/be_kind_of_spec.rb} +0 -0
  71. data/spec/rspec/matchers/{be_spec.rb → built_in/be_spec.rb} +76 -32
  72. data/spec/rspec/matchers/{be_within_spec.rb → built_in/be_within_spec.rb} +6 -2
  73. data/spec/rspec/matchers/{change_spec.rb → built_in/change_spec.rb} +310 -69
  74. data/spec/rspec/matchers/built_in/compound_spec.rb +292 -0
  75. data/spec/rspec/matchers/built_in/contain_exactly_spec.rb +441 -0
  76. data/spec/rspec/matchers/{cover_spec.rb → built_in/cover_spec.rb} +0 -0
  77. data/spec/rspec/matchers/built_in/eq_spec.rb +156 -0
  78. data/spec/rspec/matchers/{eql_spec.rb → built_in/eql_spec.rb} +2 -2
  79. data/spec/rspec/matchers/built_in/equal_spec.rb +106 -0
  80. data/spec/rspec/matchers/{exist_spec.rb → built_in/exist_spec.rb} +1 -1
  81. data/spec/rspec/matchers/{has_spec.rb → built_in/has_spec.rb} +39 -0
  82. data/spec/rspec/matchers/{include_spec.rb → built_in/include_spec.rb} +118 -109
  83. data/spec/rspec/matchers/{match_spec.rb → built_in/match_spec.rb} +30 -2
  84. data/spec/rspec/matchers/{operator_matcher_spec.rb → built_in/operators_spec.rb} +26 -26
  85. data/spec/rspec/matchers/built_in/output_spec.rb +165 -0
  86. data/spec/rspec/matchers/{raise_error_spec.rb → built_in/raise_error_spec.rb} +81 -11
  87. data/spec/rspec/matchers/{respond_to_spec.rb → built_in/respond_to_spec.rb} +0 -0
  88. data/spec/rspec/matchers/{satisfy_spec.rb → built_in/satisfy_spec.rb} +0 -0
  89. data/spec/rspec/matchers/{start_with_end_with_spec.rb → built_in/start_and_end_with_spec.rb} +82 -15
  90. data/spec/rspec/matchers/{throw_symbol_spec.rb → built_in/throw_symbol_spec.rb} +29 -10
  91. data/spec/rspec/matchers/{yield_spec.rb → built_in/yield_spec.rb} +90 -0
  92. data/spec/rspec/matchers/configuration_spec.rb +7 -39
  93. data/spec/rspec/matchers/description_generation_spec.rb +22 -6
  94. data/spec/rspec/matchers/dsl_spec.rb +838 -0
  95. data/spec/rspec/matchers/legacy_spec.rb +101 -0
  96. data/spec/rspec/matchers_spec.rb +74 -0
  97. data/spec/spec_helper.rb +35 -21
  98. data/spec/support/shared_examples.rb +26 -4
  99. metadata +172 -116
  100. metadata.gz.sig +3 -4
  101. checksums.yaml +0 -15
  102. checksums.yaml.gz.sig +0 -0
  103. data/features/built_in_matchers/match_array.feature +0 -37
  104. data/lib/rspec/expectations/errors.rb +0 -9
  105. data/lib/rspec/expectations/extensions.rb +0 -1
  106. data/lib/rspec/expectations/extensions/object.rb +0 -29
  107. data/lib/rspec/matchers/built_in/match_array.rb +0 -51
  108. data/lib/rspec/matchers/compatibility.rb +0 -14
  109. data/lib/rspec/matchers/matcher.rb +0 -301
  110. data/lib/rspec/matchers/method_missing.rb +0 -12
  111. data/lib/rspec/matchers/operator_matcher.rb +0 -99
  112. data/lib/rspec/matchers/test_unit_integration.rb +0 -11
  113. data/spec/rspec/matchers/eq_spec.rb +0 -60
  114. data/spec/rspec/matchers/equal_spec.rb +0 -78
  115. data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
  116. data/spec/rspec/matchers/match_array_spec.rb +0 -194
  117. data/spec/rspec/matchers/matcher_spec.rb +0 -706
  118. data/spec/rspec/matchers/matchers_spec.rb +0 -36
  119. data/spec/rspec/matchers/method_missing_spec.rb +0 -28
  120. data/spec/support/classes.rb +0 -56
  121. data/spec/support/in_sub_process.rb +0 -37
  122. data/spec/support/ruby_version.rb +0 -10
@@ -2,35 +2,36 @@ module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
4
  class Has
5
- def initialize(expected, *args)
6
- @expected, @args = expected, args
5
+ include Composable
6
+
7
+ def initialize(method_name, *args, &block)
8
+ @method_name, @args, @block = method_name, args, block
7
9
  end
8
10
 
9
- def matches?(actual)
10
- actual.__send__(predicate(@expected), *@args)
11
+ def matches?(actual, &block)
12
+ actual.__send__(predicate, *@args, &(@block || block))
11
13
  end
12
- alias == matches?
13
14
 
14
- def failure_message_for_should
15
- "expected ##{predicate(@expected)}#{failure_message_args_description} to return true, got false"
15
+ def failure_message
16
+ "expected ##{predicate}#{failure_message_args_description} to return true, got false"
16
17
  end
17
18
 
18
- def failure_message_for_should_not
19
- "expected ##{predicate(@expected)}#{failure_message_args_description} to return false, got true"
19
+ def failure_message_when_negated
20
+ "expected ##{predicate}#{failure_message_args_description} to return false, got true"
20
21
  end
21
22
 
22
23
  def description
23
- [method_description(@expected), args_description].compact.join(' ')
24
+ [method_description, args_description].compact.join(' ')
24
25
  end
25
26
 
26
- private
27
+ private
27
28
 
28
- def predicate(sym)
29
- "#{sym.to_s.sub("have_","has_")}?".to_sym
29
+ def predicate
30
+ @predicate ||= :"has_#{@method_name.to_s.match(Matchers::HAS_REGEX).captures.first}?"
30
31
  end
31
32
 
32
- def method_description(method)
33
- method.to_s.gsub('_', ' ')
33
+ def method_description
34
+ @method_name.to_s.gsub('_', ' ')
34
35
  end
35
36
 
36
37
  def args_description
@@ -8,52 +8,74 @@ module RSpec
8
8
 
9
9
  def matches?(actual)
10
10
  @actual = actual
11
- perform_match(:all?, :all?, @actual, @expected)
11
+ perform_match(:all?, :all?)
12
12
  end
13
13
 
14
14
  def does_not_match?(actual)
15
15
  @actual = actual
16
- perform_match(:none?, :any?, @actual, @expected)
16
+ perform_match(:none?, :any?)
17
17
  end
18
18
 
19
19
  def description
20
- "include#{expected_to_sentence}"
20
+ described_items = surface_descriptions_in(expected)
21
+ improve_hash_formatting "include#{to_sentence(described_items)}"
22
+ end
23
+
24
+ def failure_message
25
+ improve_hash_formatting(super)
26
+ end
27
+
28
+ def failure_message_when_negated
29
+ improve_hash_formatting(super)
21
30
  end
22
31
 
23
32
  def diffable?
24
- # Matchers do not diff well, since diff uses their inspect
25
- # output, which includes their instance variables and such.
26
- @expected.none? { |e| RSpec::Matchers.is_a_matcher?(e) }
33
+ true
27
34
  end
28
35
 
29
36
  private
30
37
 
31
- def perform_match(predicate, hash_predicate, actuals, expecteds)
32
- expecteds.__send__(predicate) do |expected|
33
- if comparing_hash_values?(actuals, expected)
34
- expected.__send__(hash_predicate) { |k,v|
35
- actuals.has_key?(k) && actuals[k] == v
36
- }
37
- elsif comparing_hash_keys?(actuals, expected)
38
- actuals.has_key?(expected)
39
- elsif comparing_with_matcher?(actual, expected)
40
- actual.any? { |value| expected.matches?(value) }
38
+ def perform_match(predicate, hash_subset_predicate)
39
+ expected.__send__(predicate) do |expected_item|
40
+ if comparing_hash_to_a_subset?(expected_item)
41
+ expected_item.__send__(hash_subset_predicate) do |(key, value)|
42
+ actual_hash_includes?(key, value)
43
+ end
44
+ elsif comparing_hash_keys?(expected_item)
45
+ actual_hash_has_key?(expected_item)
41
46
  else
42
- actuals.include?(expected)
47
+ actual_collection_includes?(expected_item)
43
48
  end
44
49
  end
45
50
  end
46
51
 
47
- def comparing_hash_keys?(actual, expected)
48
- actual.is_a?(Hash) && !expected.is_a?(Hash)
52
+ def comparing_hash_to_a_subset?(expected_item)
53
+ actual.is_a?(Hash) && expected_item.is_a?(Hash)
54
+ end
55
+
56
+ def actual_hash_includes?(expected_key, expected_value)
57
+ actual_value = actual.fetch(expected_key) { return false }
58
+ values_match?(expected_value, actual_value)
59
+ end
60
+
61
+ def comparing_hash_keys?(expected_item)
62
+ actual.is_a?(Hash) && !expected_item.is_a?(Hash)
49
63
  end
50
64
 
51
- def comparing_hash_values?(actual, expected)
52
- actual.is_a?(Hash) && expected.is_a?(Hash)
65
+ def actual_hash_has_key?(expected_key)
66
+ # We check `has_key?` first for perf:
67
+ # has_key? is O(1), but `any?` is O(N).
68
+ actual.has_key?(expected_key) ||
69
+ actual.keys.any? { |key| values_match?(expected_key, key) }
53
70
  end
54
71
 
55
- def comparing_with_matcher?(actual, expected)
56
- actual.is_a?(Array) && RSpec::Matchers.is_a_matcher?(expected)
72
+ def actual_collection_includes?(expected_item)
73
+ return true if actual.include?(expected_item)
74
+
75
+ # String lacks an `any?` method...
76
+ return false unless actual.respond_to?(:any?)
77
+
78
+ actual.any? { |value| values_match?(expected_item, value) }
57
79
  end
58
80
  end
59
81
  end
@@ -2,15 +2,18 @@ module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
4
  class Match < BaseMatcher
5
-
6
5
  def match(expected, actual)
7
- actual.match expected
6
+ return true if values_match?(expected, actual)
7
+ actual.match(expected) if actual.respond_to?(:match)
8
+ end
9
+
10
+ def description
11
+ "match #{surface_descriptions_in(expected).inspect}"
8
12
  end
9
13
 
10
14
  def diffable?
11
15
  true
12
16
  end
13
-
14
17
  end
15
18
  end
16
19
  end
@@ -0,0 +1,103 @@
1
+ require 'rspec/support'
2
+
3
+ module RSpec
4
+ module Matchers
5
+ module BuiltIn
6
+ class OperatorMatcher
7
+ class << self
8
+ def registry
9
+ @registry ||= {}
10
+ end
11
+
12
+ def register(klass, operator, matcher)
13
+ registry[klass] ||= {}
14
+ registry[klass][operator] = matcher
15
+ end
16
+
17
+ def unregister(klass, operator)
18
+ registry[klass] && registry[klass].delete(operator)
19
+ end
20
+
21
+ def get(klass, operator)
22
+ klass.ancestors.each { |ancestor|
23
+ matcher = registry[ancestor] && registry[ancestor][operator]
24
+ return matcher if matcher
25
+ }
26
+
27
+ nil
28
+ end
29
+ end
30
+
31
+ register Enumerable, '=~', BuiltIn::ContainExactly
32
+
33
+ def initialize(actual)
34
+ @actual = actual
35
+ end
36
+
37
+ def self.use_custom_matcher_or_delegate(operator)
38
+ define_method(operator) do |expected|
39
+ if uses_generic_implementation_of?(operator) && matcher = OperatorMatcher.get(@actual.class, operator)
40
+ @actual.__send__(::RSpec::Matchers.last_expectation_handler.should_method, matcher.new(expected))
41
+ else
42
+ eval_match(@actual, operator, expected)
43
+ end
44
+ end
45
+
46
+ negative_operator = operator.sub(/^=/, '!')
47
+ if negative_operator != operator && respond_to?(negative_operator)
48
+ define_method(negative_operator) do |expected|
49
+ opposite_should = ::RSpec::Matchers.last_expectation_handler.opposite_should_method
50
+ raise "RSpec does not support `#{::RSpec::Matchers.last_expectation_handler.should_method} #{negative_operator} expected`. " +
51
+ "Use `#{opposite_should} #{operator} expected` instead."
52
+ end
53
+ end
54
+ end
55
+
56
+ ['==', '===', '=~', '>', '>=', '<', '<='].each do |operator|
57
+ use_custom_matcher_or_delegate operator
58
+ end
59
+
60
+ def fail_with_message(message)
61
+ RSpec::Expectations.fail_with(message, @expected, @actual)
62
+ end
63
+
64
+ def description
65
+ "#{@operator} #{@expected.inspect}"
66
+ end
67
+
68
+ private
69
+
70
+ def uses_generic_implementation_of?(op)
71
+ Support.method_handle_for(@actual, op).owner == ::Kernel
72
+ rescue NameError
73
+ false
74
+ end
75
+
76
+ def eval_match(actual, operator, expected)
77
+ ::RSpec::Matchers.last_matcher = self
78
+ @operator, @expected = operator, expected
79
+ __delegate_operator(actual, operator, expected)
80
+ end
81
+ end
82
+
83
+ class PositiveOperatorMatcher < OperatorMatcher
84
+ def __delegate_operator(actual, operator, expected)
85
+ if actual.__send__(operator, expected)
86
+ true
87
+ elsif ['==','===', '=~'].include?(operator)
88
+ fail_with_message("expected: #{expected.inspect}\n got: #{actual.inspect} (using #{operator})")
89
+ else
90
+ fail_with_message("expected: #{operator} #{expected.inspect}\n got: #{operator.gsub(/./, ' ')} #{actual.inspect}")
91
+ end
92
+ end
93
+ end
94
+
95
+ class NegativeOperatorMatcher < OperatorMatcher
96
+ def __delegate_operator(actual, operator, expected)
97
+ return false unless actual.__send__(operator, expected)
98
+ return fail_with_message("expected not: #{operator} #{expected.inspect}\n got: #{operator.gsub(/./, ' ')} #{actual.inspect}")
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,108 @@
1
+ require 'stringio'
2
+
3
+ module RSpec
4
+ module Matchers
5
+ module BuiltIn
6
+ class Output < BaseMatcher
7
+ def initialize(expected)
8
+ @expected = expected
9
+ @stream_capturer = NullCapture
10
+ end
11
+
12
+ def matches?(block)
13
+ @actual = @stream_capturer.capture(block)
14
+
15
+ @expected ? values_match?(@expected, @actual) : captured?
16
+ end
17
+
18
+ def to_stdout
19
+ @stream_capturer = CaptureStdout
20
+ self
21
+ end
22
+
23
+ def to_stderr
24
+ @stream_capturer = CaptureStderr
25
+ self
26
+ end
27
+
28
+ def failure_message
29
+ "expected block to #{description}, #{actual_description}"
30
+ end
31
+
32
+ def failure_message_when_negated
33
+ "expected block to not #{description}, but did"
34
+ end
35
+
36
+ def description
37
+ if @expected
38
+ "output #{description_of @expected} to #{@stream_capturer.name}"
39
+ else
40
+ "output to #{@stream_capturer.name}"
41
+ end
42
+ end
43
+
44
+ def diffable?
45
+ true
46
+ end
47
+
48
+ private
49
+
50
+ def captured?
51
+ @actual.length > 0
52
+ end
53
+
54
+ def actual_description
55
+ @expected ? "but output #{captured? ? @actual.inspect : 'nothing'}" : "but did not"
56
+ end
57
+ end
58
+
59
+ module NullCapture
60
+ def self.name
61
+ "some stream"
62
+ end
63
+
64
+ def self.capture(block)
65
+ raise "You must chain `to_stdout` or `to_stderr` off of the `output(...)` matcher."
66
+ end
67
+ end
68
+
69
+ module CaptureStdout
70
+ def self.name
71
+ 'stdout'
72
+ end
73
+
74
+ def self.capture(block)
75
+ captured_stream = StringIO.new
76
+
77
+ original_stream = $stdout
78
+ $stdout = captured_stream
79
+
80
+ block.call
81
+
82
+ captured_stream.string
83
+ ensure
84
+ $stdout = original_stream
85
+ end
86
+ end
87
+
88
+ module CaptureStderr
89
+ def self.name
90
+ 'stderr'
91
+ end
92
+
93
+ def self.capture(block)
94
+ captured_stream = StringIO.new
95
+
96
+ original_stream = $stderr
97
+ $stderr = captured_stream
98
+
99
+ block.call
100
+
101
+ captured_stream.string
102
+ ensure
103
+ $stderr = original_stream
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -2,6 +2,8 @@ module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
4
  class RaiseError
5
+ include Composable
6
+
5
7
  def initialize(expected_error_or_message=Exception, expected_message=nil, &block)
6
8
  @block = block
7
9
  @actual_error = nil
@@ -34,7 +36,7 @@ module RSpec
34
36
  begin
35
37
  given_proc.call
36
38
  rescue Exception => @actual_error
37
- if @actual_error == @expected_error || @expected_error === @actual_error
39
+ if values_match?(@expected_error, @actual_error)
38
40
  @raised_expected_error = true
39
41
  @with_expected_message = verify_message
40
42
  end
@@ -47,8 +49,6 @@ module RSpec
47
49
  expectation_matched?
48
50
  end
49
51
 
50
- alias == matches?
51
-
52
52
  def expectation_matched?
53
53
  error_and_message_match? && block_matches?
54
54
  end
@@ -77,21 +77,15 @@ module RSpec
77
77
  end
78
78
 
79
79
  def verify_message
80
- case @expected_message
81
- when nil
82
- true
83
- when Regexp
84
- @expected_message =~ @actual_error.message
85
- else
86
- @expected_message == @actual_error.message
87
- end
80
+ return true if @expected_message.nil?
81
+ values_match?(@expected_message, @actual_error.message)
88
82
  end
89
83
 
90
- def failure_message_for_should
84
+ def failure_message
91
85
  @eval_block ? @actual_error.message : "expected #{expected_error}#{given_error}"
92
86
  end
93
87
 
94
- def failure_message_for_should_not
88
+ def failure_message_when_negated
95
89
  "expected no #{expected_error}#{given_error}"
96
90
  end
97
91
 
@@ -118,11 +112,11 @@ module RSpec
118
112
  def expected_error
119
113
  case @expected_message
120
114
  when nil
121
- @expected_error.inspect
115
+ description_of(@expected_error)
122
116
  when Regexp
123
117
  "#{@expected_error} with message matching #{@expected_message.inspect}"
124
118
  else
125
- "#{@expected_error} with #{@expected_message.inspect}"
119
+ "#{@expected_error} with #{description_of @expected_message}"
126
120
  end
127
121
  end
128
122