rspec-expectations 3.0.0.beta1 → 3.0.0.beta2

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