rspec-expectations 2.13.0 → 2.14.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.
Files changed (64) hide show
  1. data/Changelog.md +46 -0
  2. data/README.md +43 -87
  3. data/features/README.md +8 -9
  4. data/features/built_in_matchers/README.md +41 -41
  5. data/features/built_in_matchers/be_within.feature +3 -3
  6. data/features/built_in_matchers/expect_change.feature +6 -6
  7. data/features/built_in_matchers/expect_error.feature +2 -2
  8. data/features/built_in_matchers/start_with.feature +1 -1
  9. data/features/built_in_matchers/throw_symbol.feature +11 -11
  10. data/features/built_in_matchers/yield.feature +18 -3
  11. data/features/custom_matchers/define_diffable_matcher.feature +1 -1
  12. data/features/custom_matchers/define_matcher_outside_rspec.feature +6 -6
  13. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +1 -1
  14. data/features/customized_message.feature +1 -1
  15. data/features/support/env.rb +10 -1
  16. data/features/syntax_configuration.feature +3 -0
  17. data/features/test_frameworks/test_unit.feature +15 -17
  18. data/lib/rspec/expectations/deprecation.rb +12 -33
  19. data/lib/rspec/expectations/differ.rb +25 -7
  20. data/lib/rspec/expectations/expectation_target.rb +7 -8
  21. data/lib/rspec/expectations/extensions/object.rb +2 -12
  22. data/lib/rspec/expectations/fail_with.rb +11 -1
  23. data/lib/rspec/expectations/handler.rb +11 -6
  24. data/lib/rspec/expectations/syntax.rb +2 -2
  25. data/lib/rspec/expectations/version.rb +1 -1
  26. data/lib/rspec/expectations.rb +1 -1
  27. data/lib/rspec/matchers/be_close.rb +1 -1
  28. data/lib/rspec/matchers/built_in/be.rb +2 -0
  29. data/lib/rspec/matchers/built_in/be_within.rb +1 -1
  30. data/lib/rspec/matchers/built_in/change.rb +9 -1
  31. data/lib/rspec/matchers/built_in/have.rb +20 -4
  32. data/lib/rspec/matchers/built_in/include.rb +2 -10
  33. data/lib/rspec/matchers/built_in/raise_error.rb +23 -7
  34. data/lib/rspec/matchers/built_in/yield.rb +78 -3
  35. data/lib/rspec/matchers/operator_matcher.rb +1 -1
  36. data/lib/rspec/matchers/pretty.rb +7 -1
  37. data/lib/rspec/matchers/test_unit_integration.rb +11 -0
  38. data/lib/rspec/matchers.rb +141 -143
  39. data/spec/rspec/expectations/differ_spec.rb +27 -5
  40. data/spec/rspec/expectations/expectation_target_spec.rb +10 -3
  41. data/spec/rspec/expectations/extensions/kernel_spec.rb +5 -5
  42. data/spec/rspec/expectations/fail_with_spec.rb +19 -0
  43. data/spec/rspec/expectations/handler_spec.rb +42 -21
  44. data/spec/rspec/expectations/syntax_spec.rb +45 -3
  45. data/spec/rspec/matchers/be_close_spec.rb +6 -6
  46. data/spec/rspec/matchers/be_spec.rb +36 -36
  47. data/spec/rspec/matchers/be_within_spec.rb +8 -0
  48. data/spec/rspec/matchers/change_spec.rb +17 -6
  49. data/spec/rspec/matchers/configuration_spec.rb +57 -89
  50. data/spec/rspec/matchers/description_generation_spec.rb +16 -2
  51. data/spec/rspec/matchers/exist_spec.rb +9 -9
  52. data/spec/rspec/matchers/has_spec.rb +1 -1
  53. data/spec/rspec/matchers/have_spec.rb +12 -2
  54. data/spec/rspec/matchers/include_matcher_integration_spec.rb +2 -2
  55. data/spec/rspec/matchers/include_spec.rb +4 -4
  56. data/spec/rspec/matchers/match_array_spec.rb +1 -1
  57. data/spec/rspec/matchers/match_spec.rb +1 -1
  58. data/spec/rspec/matchers/raise_error_spec.rb +189 -99
  59. data/spec/rspec/matchers/respond_to_spec.rb +4 -4
  60. data/spec/rspec/matchers/satisfy_spec.rb +1 -1
  61. data/spec/rspec/matchers/start_with_end_with_spec.rb +2 -2
  62. data/spec/rspec/matchers/yield_spec.rb +81 -4
  63. data/spec/spec_helper.rb +1 -1
  64. metadata +8 -7
@@ -1,5 +1,14 @@
1
1
  require 'aruba/cucumber'
2
2
 
3
+ timeouts = { 'java' => 60 }
4
+
3
5
  Before do
4
- @aruba_timeout_seconds = 15
6
+ @aruba_timeout_seconds = timeouts.fetch(RUBY_PLATFORM) { 15 }
5
7
  end
8
+
9
+ Aruba.configure do |config|
10
+ config.before_cmd do |cmd|
11
+ set_env('JRUBY_OPTS', "-X-C #{ENV['JRUBY_OPTS']}") # disable JIT since these processes are so short lived
12
+ end
13
+ end if RUBY_PLATFORM == 'java'
14
+
@@ -47,6 +47,9 @@ Feature: Syntax Configuration
47
47
  config.expect_with :rspec do |c|
48
48
  c.syntax = :should
49
49
  end
50
+ config.mock_with :rspec do |c|
51
+ c.syntax = :should
52
+ end
50
53
  end
51
54
  """
52
55
  When I run `rspec disable_expect_syntax.rb syntaxes_spec.rb`
@@ -1,11 +1,12 @@
1
1
  Feature: Test::Unit integration
2
2
 
3
- RSpec-expectations is a stand-alone gem that can be used without
4
- the rest of RSpec. It can easily be used with another test
5
- framework such as Test::Unit if you like RSpec's should/should_not
6
- syntax but prefer the test organization of another framework.
3
+ RSpec-expectations is a stand-alone gem that can be used without the rest of
4
+ RSpec. If you like the way Test::Unit (or MiniTest) organizes tests, but
5
+ prefer RSpec's approach to expressing expectations, you can have both.
7
6
 
8
- Scenario: Basic Test::Unit usage
7
+ The one downside is that failures are reported as errors with MiniTest.
8
+
9
+ Scenario: use rspec/expectations with Test::Unit
9
10
  Given a file named "rspec_expectations_test.rb" with:
10
11
  """ruby
11
12
  require 'test/unit'
@@ -17,30 +18,27 @@ Feature: Test::Unit integration
17
18
  end
18
19
 
19
20
  def be_an_int
20
- RSpec.deprecate(:be_an_int, :be_an_integer)
21
+ # This is actually an internal rspec-expectations API, but is used
22
+ # here to demonstrate that deprecation warnings from within
23
+ # rspec-expectations work correcty without depending on rspec-core
24
+ RSpec.deprecate(:be_an_int, :replacement => :be_an_integer)
21
25
  be_an_integer
22
26
  end
23
27
 
24
28
  def test_passing_expectation
25
- x = 1 + 3
26
- x.should == 4
29
+ expect(1 + 3).to eq 4
27
30
  end
28
31
 
29
32
  def test_failing_expectation
30
- array = [1, 2]
31
- array.should be_empty
32
- end
33
-
34
- def test_expect_matcher
35
- expect { @a = 5 }.to change { @a }.from(nil).to(5)
33
+ expect([1,2]).to be_empty
36
34
  end
37
35
 
38
- def test_custom_matcher_and_deprecation_warning
39
- 1.should be_an_int
36
+ def test_custom_matcher_with_deprecation_warning
37
+ expect(1).to be_an_int
40
38
  end
41
39
  end
42
40
  """
43
41
  When I run `ruby rspec_expectations_test.rb`
44
- Then the output should contain "4 tests, 0 assertions, 1 failures, 0 errors" or "4 tests, 0 assertions, 0 failures, 1 errors"
42
+ Then the output should contain "3 tests, 0 assertions, 0 failures, 1 errors" or "3 tests, 0 assertions, 1 failures, 0 errors"
45
43
  And the output should contain "expected empty? to return true, got false"
46
44
  And the output should contain "be_an_int is deprecated"
@@ -1,38 +1,17 @@
1
1
  module RSpec
2
- unless respond_to?(:deprecate)
3
- class << self
4
- # Used internally by RSpec to display standard deprecation warnings.
5
- # This is also defined in rspec-core, but we can't assume it's loaded
6
- # since rspec-expectations should be usable w/o rspec-core.
7
- def deprecate(method, alternate_method=nil, version=nil)
8
- version_string = version ? "rspec-#{version}" : "a future version of RSpec"
9
-
10
- message = <<-NOTICE
11
-
12
- *****************************************************************
13
- DEPRECATION WARNING: you are using deprecated behaviour that will
14
- be removed from #{version_string}.
15
-
16
- #{caller(0)[2]}
17
-
18
- * #{method} is deprecated.
19
- NOTICE
20
- if alternate_method
21
- message << <<-ADDITIONAL
22
- * please use #{alternate_method} instead.
23
- ADDITIONAL
24
- end
25
-
26
- message << "*****************************************************************"
27
- warn_deprecation(message)
28
- end
29
-
30
- # Used internally by RSpec to display custom deprecation warnings. This
31
- # is also defined in rspec-core, but we can't assume it's loaded since
32
- # rspec-expectations should be usable w/o rspec-core.
33
- def warn_deprecation(message)
34
- warn(message)
2
+ module Expectations
3
+ module Deprecation
4
+ # @private
5
+ #
6
+ # Used internally to print deprecation warnings
7
+ def deprecate(deprecated, options={})
8
+ message = "DEPRECATION: #{deprecated} is deprecated."
9
+ message << " Use #{options[:replacement]} instead." if options[:replacement]
10
+ message << " Called from #{caller(0)[2]}."
11
+ warn message
35
12
  end
36
13
  end
37
14
  end
15
+
16
+ extend(Expectations::Deprecation) unless respond_to?(:deprecate)
38
17
  end
@@ -6,11 +6,11 @@ module RSpec
6
6
  module Expectations
7
7
  class Differ
8
8
  # This is snagged from diff/lcs/ldiff.rb (which is a commandline tool)
9
- def diff_as_string(data_new, data_old)
10
- data_old = data_old.split(/\n/).map! { |e| e.chomp }
11
- data_new = data_new.split(/\n/).map! { |e| e.chomp }
9
+ def diff_as_string(input_data_new, input_data_old)
10
+ output = matching_encoding("", input_data_old)
11
+ data_old = input_data_old.split(matching_encoding("\n", input_data_old)).map! { |e| e.chomp }
12
+ data_new = input_data_new.split(matching_encoding("\n", input_data_new)).map! { |e| e.chomp }
12
13
  diffs = Diff::LCS.diff(data_old, data_new)
13
- output = ""
14
14
  return output if diffs.empty?
15
15
  oldhunk = hunk = nil
16
16
  file_length_difference = 0
@@ -33,16 +33,24 @@ module RSpec
33
33
  hunk.unshift(oldhunk)
34
34
  end
35
35
  else
36
- output << oldhunk.diff(format)
36
+ output << matching_encoding(oldhunk.diff(format).to_s, output)
37
37
  end
38
38
  ensure
39
39
  oldhunk = hunk
40
- output << "\n"
40
+ output << matching_encoding("\n", output)
41
41
  end
42
42
  end
43
43
  #Handle the last remaining hunk
44
- output << oldhunk.diff(format) << "\n"
44
+ output << matching_encoding(oldhunk.diff(format).to_s,output)
45
+ output << matching_encoding("\n",output)
45
46
  color_diff output
47
+ rescue Encoding::CompatibilityError
48
+ if input_data_new.encoding != input_data_old.encoding
49
+ "Could not produce a diff because the encoding of the actual string (#{input_data_old.encoding}) "+
50
+ "differs from the encoding of the expected string (#{input_data_new.encoding})"
51
+ else
52
+ "Could not produce a diff because of the encoding of the string (#{input_data_old.encoding})"
53
+ end
46
54
  end
47
55
 
48
56
  def diff_as_object(actual, expected)
@@ -108,6 +116,16 @@ module RSpec
108
116
  PP.pp(object,"")
109
117
  end
110
118
  end
119
+
120
+ if String.method_defined?(:encoding)
121
+ def matching_encoding(string, source)
122
+ string.encode(source.encoding)
123
+ end
124
+ else
125
+ def matching_encoding(string, source)
126
+ string
127
+ end
128
+ end
111
129
  end
112
130
 
113
131
  end
@@ -7,8 +7,8 @@ module RSpec
7
7
  # # used with `to`
8
8
  # expect(actual).to eq(3)
9
9
  #
10
- # # with `to_not`
11
- # expect(actual).to_not eq(3)
10
+ # # with `not_to`
11
+ # expect(actual).not_to eq(3)
12
12
  class ExpectationTarget
13
13
  class << self
14
14
  attr_accessor :deprecated_should_enabled
@@ -36,29 +36,28 @@ module RSpec
36
36
 
37
37
  # Runs the given expectation, passing if `matcher` returns false.
38
38
  # @example
39
- # expect(value).to_not eq(5)
40
39
  # expect(value).not_to eq(5)
41
40
  # @param [Matcher]
42
41
  # matcher
43
42
  # @param [String] message optional message to display when the expectation fails
44
43
  # @return [Boolean] false if the negative expectation succeeds (else raises)
45
44
  # @see RSpec::Matchers
46
- def to_not(matcher=nil, message=nil, &block)
47
- prevent_operator_matchers(:to_not, matcher)
45
+ def not_to(matcher=nil, message=nil, &block)
46
+ prevent_operator_matchers(:not_to, matcher)
48
47
  RSpec::Expectations::NegativeExpectationHandler.handle_matcher(@target, matcher, message, &block)
49
48
  end
50
- alias not_to to_not
49
+ alias to_not not_to
51
50
 
52
51
  def self.enable_deprecated_should
53
52
  return if deprecated_should_enabled?
54
53
 
55
54
  def should(*args)
56
- RSpec.deprecate "`expect { }.should`", "`expect { }.to`", 3
55
+ RSpec.deprecate "`expect { }.should`", :replacement => "`expect { }.to`"
57
56
  @target.should(*args)
58
57
  end
59
58
 
60
59
  def should_not(*args)
61
- RSpec.deprecate "`expect { }.should_not`", "`expect { }.to_not`", 3
60
+ RSpec.deprecate "`expect { }.should_not`", :replacement => "`expect { }.not_to`"
62
61
  @target.should_not(*args)
63
62
  end
64
63
 
@@ -6,17 +6,7 @@ module RSpec
6
6
  def const_missing(name)
7
7
  case name
8
8
  when :Rspec, :Spec
9
- RSpec.warn_deprecation <<-WARNING
10
- *****************************************************************
11
- DEPRECATION WARNING: you are using a deprecated constant that will
12
- be removed from a future version of RSpec.
13
-
14
- #{caller(0)[2]}
15
-
16
- * #{name} is deprecated.
17
- * RSpec is the new top-level module in RSpec-2
18
- ***************************************************************
19
- WARNING
9
+ RSpec.deprecate(name.to_s, :replacement => "RSpec")
20
10
  RSpec
21
11
  else
22
12
  begin
@@ -31,7 +21,7 @@ WARNING
31
21
 
32
22
  # @deprecated (no replacement)
33
23
  def differ=(ignore)
34
- RSpec.deprecate("RSpec::Expectations.differ=(differ)", "nothing at all (diffing is now automatic and no longer configurable)")
24
+ RSpec.deprecate("RSpec::Expectations.differ=(differ)")
35
25
  end
36
26
  end
37
27
  end
@@ -52,7 +52,17 @@ module RSpec
52
52
 
53
53
  def coerce_to_string(string_or_array)
54
54
  return string_or_array unless Array === string_or_array
55
- string_or_array.join(',')
55
+ diffably_stringify(string_or_array).join("\n")
56
+ end
57
+
58
+ def diffably_stringify(array)
59
+ array.map do |entry|
60
+ if Array === entry
61
+ entry.inspect
62
+ else
63
+ entry.to_s.gsub("\n", "\\n")
64
+ end
65
+ end
56
66
  end
57
67
 
58
68
  if String.method_defined?(:encoding)
@@ -2,13 +2,14 @@ module RSpec
2
2
  module Expectations
3
3
 
4
4
  class ExpectationHandler
5
- def self.message_must_be_string(msg)
6
- "WARNING: ignoring the provided expectation message argument " +
7
- "(#{msg.inspect}) since it is not a string."
8
- end
9
-
10
5
  def self.check_message(msg)
11
- ::Kernel.warn message_must_be_string(msg) unless msg.nil? || msg.is_a?(String)
6
+ unless msg.nil? || msg.respond_to?(:to_str) || msg.respond_to?(:call)
7
+ ::Kernel.warn [
8
+ "WARNING: ignoring the provided expectation message argument (",
9
+ msg.inspect,
10
+ ") since it is not a string or a proc."
11
+ ].join
12
+ end
12
13
  end
13
14
  end
14
15
 
@@ -23,6 +24,8 @@ module RSpec
23
24
  match = matcher.matches?(actual, &block)
24
25
  return match if match
25
26
 
27
+ message = message.call if message.respond_to?(:call)
28
+
26
29
  message ||= matcher.respond_to?(:failure_message_for_should) ?
27
30
  matcher.failure_message_for_should :
28
31
  matcher.failure_message
@@ -47,6 +50,8 @@ module RSpec
47
50
  matcher.matches?(actual, &block)
48
51
  return match unless match
49
52
 
53
+ message = message.call if message.respond_to?(:call)
54
+
50
55
  message ||= matcher.respond_to?(:failure_message_for_should_not) ?
51
56
  matcher.failure_message_for_should_not :
52
57
  matcher.negative_failure_message
@@ -32,10 +32,10 @@ module RSpec
32
32
  # `ExpectationTarget`.
33
33
  # @example
34
34
  # expect(actual).to eq(expected)
35
- # expect(actual).to_not eq(expected)
35
+ # expect(actual).not_to eq(expected)
36
36
  # @return [ExpectationTarget]
37
37
  # @see ExpectationTarget#to
38
- # @see ExpectationTarget#to_not
38
+ # @see ExpectationTarget#not_to
39
39
 
40
40
  # @api private
41
41
  # Determines where we add `should` and `should_not`.
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Expectations
3
3
  # @private
4
4
  module Version
5
- STRING = '2.13.0'
5
+ STRING = '2.14.0'
6
6
  end
7
7
  end
8
8
  end
@@ -37,7 +37,7 @@ module RSpec
37
37
  # `eq.failure_message_for_should_not`.
38
38
  #
39
39
  # rspec-expectations ships with a standard set of useful matchers, and writing
40
- # your own matchers is quite simple.
40
+ # your own matchers is quite simple.
41
41
  #
42
42
  # See [RSpec::Matchers](../RSpec/Matchers) for more information about the
43
43
  # built-in matchers that ship with rspec-expectations, and how to write your
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Matchers
3
3
  # @deprecated use +be_within+ instead.
4
4
  def be_close(expected, delta)
5
- RSpec.deprecate("be_close(#{expected}, #{delta})", "be_within(#{delta}).of(#{expected})")
5
+ RSpec.deprecate("be_close(#{expected}, #{delta})", :replacement => "be_within(#{delta}).of(#{expected})")
6
6
  be_within(delta).of(expected)
7
7
  end
8
8
  end
@@ -148,6 +148,8 @@ it is a bit confusing.
148
148
  end
149
149
  end
150
150
 
151
+ alias === matches?
152
+
151
153
  def failure_message_for_should
152
154
  "expected #{predicate}#{args_to_s} to return true, got #{@result.inspect}"
153
155
  end
@@ -23,7 +23,7 @@ module RSpec
23
23
 
24
24
  def percent_of(expected)
25
25
  @expected = expected
26
- @tolerance = @delta * @expected / 100
26
+ @tolerance = @delta * @expected.abs / 100.0
27
27
  @unit = '%'
28
28
  self
29
29
  end
@@ -39,7 +39,7 @@ MESSAGE
39
39
  if @eval_before && !expected_matches_actual?(@expected_before, @actual_before)
40
40
  "#{message} should have initially been #{@expected_before.inspect}, but was #{@actual_before.inspect}"
41
41
  elsif @eval_after && !expected_matches_actual?(@expected_after, @actual_after)
42
- "#{message} should have been changed to #{@expected_after.inspect}, but is now #{@actual_after.inspect}"
42
+ "#{message} should have been changed to #{failure_message_for_expected_after}, but is now #{@actual_after.inspect}"
43
43
  elsif @expected_delta
44
44
  "#{message} should have been changed by #{@expected_delta.inspect}, but was changed by #{actual_delta.inspect}"
45
45
  elsif @minimum
@@ -92,6 +92,14 @@ MESSAGE
92
92
 
93
93
  private
94
94
 
95
+ def failure_message_for_expected_after
96
+ if RSpec::Matchers.is_a_matcher?(@expected_after)
97
+ @expected_after.description
98
+ else
99
+ @expected_after.inspect
100
+ end
101
+ end
102
+
95
103
  def message
96
104
  @message || "result"
97
105
  end
@@ -2,6 +2,8 @@ module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
4
  class Have
5
+ QUERY_METHODS = [:size, :length, :count].freeze
6
+
5
7
  def initialize(expected, relativity=:exactly)
6
8
  @expected = case expected
7
9
  when :no then 0
@@ -22,9 +24,19 @@ module RSpec
22
24
 
23
25
  def matches?(collection_or_owner)
24
26
  collection = determine_collection(collection_or_owner)
25
- query_method = determine_query_method(collection)
26
- raise not_a_collection unless query_method
27
- @actual = collection.__send__(query_method)
27
+ case collection
28
+ when enumerator_class
29
+ for query_method in QUERY_METHODS
30
+ next unless collection.respond_to?(query_method)
31
+ @actual = collection.__send__(query_method)
32
+ break unless @actual.nil?
33
+ end
34
+ raise not_a_collection if @actual.nil?
35
+ else
36
+ query_method = determine_query_method(collection)
37
+ raise not_a_collection unless query_method
38
+ @actual = collection.__send__(query_method)
39
+ end
28
40
  case @relativity
29
41
  when :at_least then @actual >= @expected
30
42
  when :at_most then @actual <= @expected
@@ -46,7 +58,7 @@ module RSpec
46
58
  end
47
59
 
48
60
  def determine_query_method(collection)
49
- [:size, :length, :count].detect {|m| collection.respond_to?(m)}
61
+ QUERY_METHODS.detect {|m| collection.respond_to?(m)}
50
62
  end
51
63
 
52
64
  def not_a_collection
@@ -102,6 +114,10 @@ EOF
102
114
  def relative_expectation
103
115
  "#{relativities[@relativity]}#{@expected}"
104
116
  end
117
+
118
+ def enumerator_class
119
+ RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator
120
+ end
105
121
  end
106
122
  end
107
123
  end
@@ -23,7 +23,7 @@ module RSpec
23
23
  def diffable?
24
24
  # Matchers do not diff well, since diff uses their inspect
25
25
  # output, which includes their instance variables and such.
26
- @expected.none? { |e| is_a_matcher?(e) }
26
+ @expected.none? { |e| RSpec::Matchers.is_a_matcher?(e) }
27
27
  end
28
28
 
29
29
  private
@@ -53,15 +53,7 @@ module RSpec
53
53
  end
54
54
 
55
55
  def comparing_with_matcher?(actual, expected)
56
- actual.is_a?(Array) && is_a_matcher?(expected)
57
- end
58
-
59
- def is_a_matcher?(object)
60
- return false if object.respond_to?(:i_respond_to_everything_so_im_not_really_a_matcher)
61
-
62
- [:failure_message_for_should, :failure_message].any? do |msg|
63
- object.respond_to?(msg)
64
- end && object.respond_to?(:matches?)
56
+ actual.is_a?(Array) && RSpec::Matchers.is_a_matcher?(expected)
65
57
  end
66
58
  end
67
59
  end
@@ -14,19 +14,31 @@ module RSpec
14
14
  end
15
15
 
16
16
  def matches?(given_proc, negative_expectation = false)
17
+ if negative_expectation && (expecting_specific_exception? || @expected_message)
18
+ what_to_deprecate = if expecting_specific_exception? && @expected_message
19
+ "`expect { }.not_to raise_error(SpecificErrorClass, message)`"
20
+ elsif expecting_specific_exception?
21
+ "`expect { }.not_to raise_error(SpecificErrorClass)`"
22
+ elsif @expected_message
23
+ "`expect { }.not_to raise_error(message)`"
24
+ end
25
+ RSpec.deprecate(what_to_deprecate, :replacement => "`expect { }.not_to raise_error()`")
26
+ end
17
27
  @raised_expected_error = false
18
28
  @with_expected_message = false
19
29
  @eval_block = false
20
30
  @eval_block_passed = false
31
+ unless given_proc.respond_to?(:call)
32
+ ::Kernel.warn "`raise_error` was called with non-proc object #{given_proc.inspect}"
33
+ return false
34
+ end
21
35
  begin
22
36
  given_proc.call
23
- rescue @expected_error => @actual_error
24
- @raised_expected_error = true
25
- @with_expected_message = verify_message
26
37
  rescue Exception => @actual_error
27
- # This clause should be empty, but rcov will not report it as covered
28
- # unless something (anything) is executed within the clause
29
- "http://eigenclass.org/hiki.rb?rcov-0.8.0"
38
+ if @actual_error == @expected_error || @expected_error === @actual_error
39
+ @raised_expected_error = true
40
+ @with_expected_message = verify_message
41
+ end
30
42
  end
31
43
 
32
44
  unless negative_expectation
@@ -79,7 +91,7 @@ module RSpec
79
91
  def expected_error
80
92
  case @expected_message
81
93
  when nil
82
- @expected_error
94
+ @expected_error.inspect
83
95
  when Regexp
84
96
  "#{@expected_error} with message matching #{@expected_message.inspect}"
85
97
  else
@@ -101,6 +113,10 @@ module RSpec
101
113
  *backtrace
102
114
  ].join("\n # ")
103
115
  end
116
+
117
+ def expecting_specific_exception?
118
+ @expected_error != Exception
119
+ end
104
120
  end
105
121
  end
106
122
  end
@@ -65,17 +65,92 @@ module RSpec
65
65
  end
66
66
 
67
67
  class YieldControl < BaseMatcher
68
+ def initialize
69
+ @expectation_type = nil
70
+ @expected_yields_count = nil
71
+ end
72
+
68
73
  def matches?(block)
69
74
  probe = YieldProbe.probe(block)
70
- probe.yielded_once?(:yield_control)
75
+
76
+ if @expectation_type
77
+ probe.num_yields.send(@expectation_type, @expected_yields_count)
78
+ else
79
+ probe.yielded_once?(:yield_control)
80
+ end
81
+ end
82
+
83
+ def once
84
+ exactly(1)
85
+ self
86
+ end
87
+
88
+ def twice
89
+ exactly(2)
90
+ self
91
+ end
92
+
93
+ def exactly(number)
94
+ set_expected_yields_count(:==, number)
95
+ self
96
+ end
97
+
98
+ def at_most(number)
99
+ set_expected_yields_count(:<=, number)
100
+ self
101
+ end
102
+
103
+ def at_least(number)
104
+ set_expected_yields_count(:>=, number)
105
+ self
106
+ end
107
+
108
+ def times
109
+ self
71
110
  end
72
111
 
73
112
  def failure_message_for_should
74
- "expected given block to yield control"
113
+ 'expected given block to yield control'.tap do |failure_message|
114
+ failure_message << relativity_failure_message
115
+ end
75
116
  end
76
117
 
77
118
  def failure_message_for_should_not
78
- "expected given block not to yield control"
119
+ 'expected given block not to yield control'.tap do |failure_message|
120
+ failure_message << relativity_failure_message
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def set_expected_yields_count(relativity, n)
127
+ @expectation_type = relativity
128
+ @expected_yields_count = case n
129
+ when Numeric then n
130
+ when :once then 1
131
+ when :twice then 2
132
+ end
133
+ end
134
+
135
+ def relativity_failure_message
136
+ return '' unless @expected_yields_count
137
+ " #{human_readable_expecation_type}#{human_readable_count}"
138
+ end
139
+
140
+ def human_readable_expecation_type
141
+ case @expectation_type
142
+ when :<= then 'at most '
143
+ when :>= then 'at least '
144
+ else ''
145
+ end
146
+ end
147
+
148
+ def human_readable_count
149
+ case @expected_yields_count
150
+ when 1 then "once"
151
+ when 2 then "twice"
152
+ else "#{@expected_yields_count} times"
153
+ end
79
154
  end
80
155
  end
81
156
 
@@ -91,7 +91,7 @@ module RSpec
91
91
  if actual.__send__(operator, expected)
92
92
  true
93
93
  elsif ['==','===', '=~'].include?(operator)
94
- fail_with_message("expected: #{expected.inspect}\n got: #{actual.inspect} (using #{operator})")
94
+ fail_with_message("expected: #{expected.inspect}\n got: #{actual.inspect} (using #{operator})")
95
95
  else
96
96
  fail_with_message("expected: #{operator} #{expected.inspect}\n got: #{operator.gsub(/./, ' ')} #{actual.inspect}")
97
97
  end