mocha 0.3.3 → 0.4.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 (52) hide show
  1. data/README +4 -4
  2. data/RELEASE +32 -0
  3. data/Rakefile +3 -3
  4. data/examples/misc.rb +36 -0
  5. data/examples/mocha.rb +26 -0
  6. data/examples/stubba.rb +65 -0
  7. data/lib/mocha.rb +17 -5
  8. data/lib/{stubba → mocha}/any_instance_method.rb +2 -2
  9. data/lib/mocha/auto_verify.rb +7 -7
  10. data/lib/{stubba → mocha}/central.rb +2 -2
  11. data/lib/{stubba → mocha}/class_method.rb +7 -10
  12. data/lib/mocha/expectation.rb +88 -33
  13. data/lib/mocha/expectation_error.rb +6 -0
  14. data/lib/mocha/inspect.rb +1 -1
  15. data/lib/mocha/instance_method.rb +8 -0
  16. data/lib/mocha/metaclass.rb +1 -1
  17. data/lib/mocha/mock.rb +8 -8
  18. data/lib/mocha/mock_methods.rb +60 -16
  19. data/lib/{stubba → mocha}/object.rb +8 -10
  20. data/lib/mocha/setup_and_teardown.rb +23 -0
  21. data/lib/mocha/standalone.rb +30 -0
  22. data/lib/mocha/test_case_adapter.rb +49 -0
  23. data/lib/mocha_standalone.rb +2 -0
  24. data/lib/stubba.rb +2 -8
  25. data/test/all_tests.rb +9 -10
  26. data/test/method_definer.rb +2 -2
  27. data/test/{stubba → mocha}/any_instance_method_test.rb +1 -3
  28. data/test/mocha/auto_verify_test.rb +9 -10
  29. data/test/{stubba → mocha}/central_test.rb +5 -4
  30. data/test/{stubba → mocha}/class_method_test.rb +40 -10
  31. data/test/mocha/expectation_test.rb +144 -67
  32. data/test/mocha/inspect_test.rb +12 -1
  33. data/test/mocha/metaclass_test.rb +22 -0
  34. data/test/mocha/mock_methods_test.rb +65 -7
  35. data/test/mocha/mock_test.rb +41 -4
  36. data/test/{stubba → mocha}/object_test.rb +9 -9
  37. data/test/mocha/setup_and_teardown_test.rb +76 -0
  38. data/test/mocha_acceptance_test.rb +8 -8
  39. data/test/mocha_test_result_integration_test.rb +5 -6
  40. data/test/standalone_acceptance_test.rb +110 -0
  41. data/test/stubba_acceptance_test.rb +2 -2
  42. data/test/stubba_integration_test.rb +17 -24
  43. data/test/stubba_test_result_integration_test.rb +5 -6
  44. metadata +22 -18
  45. data/lib/shared/backtracefilter.rb +0 -46
  46. data/lib/smart_test_case.rb +0 -5
  47. data/lib/smart_test_case/multiple_setup_and_teardown.rb +0 -123
  48. data/lib/stubba/instance_method.rb +0 -18
  49. data/lib/stubba/setup_and_teardown.rb +0 -25
  50. data/test/smart_test_case/multiple_setup_and_teardown_test.rb +0 -192
  51. data/test/stubba/instance_method_test.rb +0 -46
  52. data/test/stubba/setup_and_teardown_test.rb +0 -134
data/README CHANGED
@@ -1,12 +1,12 @@
1
1
  = Mocha
2
2
 
3
- Mocha is a library for mocking and stubbing within a TestCase[http://www.ruby-doc.org/core/classes/Test/Unit.html] using a syntax like that of JMock[http://www.jmock.org], and SchMock[http://rubyforge.org/projects/schmock].
3
+ Mocha is a library for mocking and stubbing using a syntax like that of JMock[http://www.jmock.org], and SchMock[http://rubyforge.org/projects/schmock]. Most commonly Mocha is used in conjunction with Test::Unit[http://www.ruby-doc.org/core/classes/Test/Unit.html], but it can be used in other contexts.
4
4
 
5
- One of its main advantages is that it allows you to mock and stub methods on _real_ (non-mock) classes and instances. You can for example stub ActiveRecord[http://api.rubyonrails.com/classes/ActiveRecord/Base.html] instance methods like +create+, +save+, +destroy+ and even class methods like +find+ to avoid hitting the database in unit tests. This is a feature that is not currently offered by other Ruby[http://www.ruby-doc.org/] mocking libraries like FlexMock[http://onestepback.org/software/flexmock] and RSpec[http://rspec.rubyforge.org].
5
+ One of its main advantages is that it allows you to mock and stub methods on _real_ (non-mock) classes and instances. You can for example stub ActiveRecord[http://api.rubyonrails.com/classes/ActiveRecord/Base.html] instance methods like +create+, +save+, +destroy+ and even class methods like +find+ to avoid hitting the database in unit tests.
6
6
 
7
- Mocha provides a unified, simple and readable syntax for both traditional mocking and for mocking with non-mock objects.
7
+ Mocha provides a unified, simple and readable syntax for both traditional mocking and for mocking with _real_ objects.
8
8
 
9
- Mocha has been harvested from projects at Reevoo[http://www.reevoo.com] by me (James[http://blog.floehopper.org]) and my colleagues Ben[http://www.reevoo.com/blogs/bengriffiths/], Chris[http://blog.seagul.co.uk] and Paul[http://po-ru.com]. Mocha is in use on real-world Rails[http://www.rubyonrails.org] projects.
9
+ Mocha has been harvested from projects at Reevoo[http://www.reevoo.com] by me (James[http://blog.floehopper.org]) and my colleagues Ben[http://www.reevoo.com/blogs/bengriffiths], Chris[http://blog.seagul.co.uk] and Paul[http://po-ru.com]. Mocha is in use on real-world Rails[http://www.rubyonrails.org] projects.
10
10
 
11
11
  == Download and Installation
12
12
 
data/RELEASE CHANGED
@@ -1,3 +1,35 @@
1
+ = 0.4.0
2
+
3
+ - Allow naming of mocks (patch from Chris Roos).
4
+ - Specify multiple return values for consecutive calls.
5
+ - Improved consistency of expectation error messages.
6
+ - Allow mocking of Object instance methods e.g. kind_of?, type.
7
+ - Provide aliased versions of #expects and #stubs to allow mocking of these methods.
8
+ - Added at_least, at_most, at_most_once methods to expectation.
9
+ - Allow expects and stubs to take a hash of method and return values.
10
+ - Eliminate warning: "instance variable @yield not initialized" (patch from Xavier Shay).
11
+ - Restore instance methods on partial mocks (patch from Chris Roos).
12
+ - Allow stubbing of a method with non-word characters in its name (patch from Paul Battley).
13
+ - Removed coupling to Test::Unit.
14
+ - Allow specified exception instance to be raised (patch from Chris Roos).
15
+ - Make mock object_id appear in hex like normal Ruby inspect (patch from Paul Battley).
16
+ - Fix path to object.rb in rdoc rake task (patch from Tomas Pospisek).
17
+ - Reverse order in which expectations are matched, so that last expectation is matched first. This allows e.g. a call to #stubs to be effectively overridden by a call to #expects (patch from Tobias Lutke).
18
+ - Stubba & SmartTestCase modules incorporated into Mocha module so only need to require 'mocha' - no longer need to require 'stubba'.
19
+ - AutoMocha removed.
20
+
21
+ = 0.3.3
22
+
23
+ - Quick bug fix to restore instance methods on partial mocks (for Kevin Clark).
24
+
25
+ = 0.3.2
26
+
27
+ - Examples added.
28
+
29
+ = 0.3.1
30
+
31
+ - Dual licensing with MIT license added.
32
+
1
33
  = 0.3.0
2
34
 
3
35
  * Rails plugin.
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require 'rake/gempackagetask'
4
4
  require 'rake/contrib/sshpublisher'
5
5
 
6
6
  module Mocha
7
- VERSION = "0.3.3"
7
+ VERSION = "0.4.0"
8
8
  end
9
9
 
10
10
  desc "Default task is currently to run all tests"
@@ -23,7 +23,7 @@ Rake::RDocTask.new do |task|
23
23
  task.rdoc_dir = 'doc'
24
24
  task.template = "html_with_google_analytics"
25
25
  task.options << "--line-numbers" << "--inline-source"
26
- task.rdoc_files.include('README', 'RELEASE', 'COPYING', 'MIT-LICENSE', 'agiledox.txt', 'lib/mocha/auto_verify.rb', 'lib/mocha/mock_methods.rb', 'lib/mocha/expectation.rb', 'lib/stubba/object.rb')
26
+ task.rdoc_files.include('README', 'RELEASE', 'COPYING', 'MIT-LICENSE', 'agiledox.txt', 'lib/mocha/auto_verify.rb', 'lib/mocha/mock_methods.rb', 'lib/mocha/expectation.rb', 'lib/mocha/object.rb')
27
27
  end
28
28
  task :rdoc => :examples
29
29
 
@@ -88,7 +88,7 @@ specification = Gem::Specification.new do |s|
88
88
  s.rdoc_options << '--title' << 'Mocha' << '--main' << 'README' << '--line-numbers'
89
89
 
90
90
  s.autorequire = 'mocha'
91
- s.files = FileList['{lib,test}/**/*.rb', '[A-Z]*'].exclude('TODO').to_a
91
+ s.files = FileList['{lib,test,examples}/**/*.rb', '[A-Z]*'].exclude('TODO').to_a
92
92
  s.test_file = "test/all_tests.rb"
93
93
  end
94
94
 
@@ -0,0 +1,36 @@
1
+ # Mocking a class method
2
+
3
+ product = Product.new
4
+ Product.expects(:find).with(1).returns(product)
5
+ assert_equal product, Product.find(1)
6
+
7
+ # Mocking an instance method on a real object
8
+
9
+ product = Product.new
10
+ product.expects(:save).returns(true)
11
+ assert product.save
12
+
13
+ # Stubbing instance methods on real object
14
+
15
+ prices = [stub(:pence => 1000), stub(:pence => 2000)]
16
+ product = Product.new
17
+ product.stubs(:prices).returns(prices)
18
+ assert_equal [1000, 2000], product.prices.collect {|p| p.pence}
19
+
20
+ # Stubbing an instance method on all instances of a class
21
+
22
+ Product.any_instance.stubs(:name).returns('stubbed_name')
23
+ product = Product.new
24
+ assert_equal 'stubbed_name', product.name
25
+
26
+ # Traditional mocking
27
+
28
+ object = mock()
29
+ object.expects(:expected_method).with(:p1, :p2).returns(:result)
30
+ assert_equal :result, object.expected_method(:p1, :p2)
31
+
32
+ # Shortcuts
33
+
34
+ object = stub(:method1 => :result1, :method2 => :result2)
35
+ assert_equal :result1, object.method1
36
+ assert_equal :result2, object.method2
@@ -0,0 +1,26 @@
1
+ class Enterprise
2
+
3
+ def initialize(dilithium)
4
+ @dilithium = dilithium
5
+ end
6
+
7
+ def go(warp_factor)
8
+ warp_factor.times { @dilithium.nuke(:anti_matter) }
9
+ end
10
+
11
+ end
12
+
13
+ require 'test/unit'
14
+ require 'rubygems'
15
+ require 'mocha'
16
+
17
+ class EnterpriseTest < Test::Unit::TestCase
18
+
19
+ def test_should_boldly_go
20
+ dilithium = mock()
21
+ dilithium.expects(:nuke).with(:anti_matter).at_least_once # auto-verified at end of test
22
+ enterprise = Enterprise.new(dilithium)
23
+ enterprise.go(2)
24
+ end
25
+
26
+ end
@@ -0,0 +1,65 @@
1
+ class Order
2
+
3
+ attr_accessor :shipped_on
4
+
5
+ def total_cost
6
+ line_items.inject(0) { |total, line_item| total + line_item.price } + shipping_cost
7
+ end
8
+
9
+ def total_weight
10
+ line_items.inject(0) { |total, line_item| total + line_item.weight }
11
+ end
12
+
13
+ def shipping_cost
14
+ total_weight * 5 + 10
15
+ end
16
+
17
+ class << self
18
+
19
+ def find_all
20
+ # Database.connection.execute('select * from orders...
21
+ end
22
+
23
+ def number_shipped_since(date)
24
+ find_all.select { |order| order.shipped_on > date }.size
25
+ end
26
+
27
+ def unshipped_value
28
+ find_all.inject(0) { |total, order| order.shipped_on ? total : total + order.total_cost }
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ require 'test/unit'
36
+ require 'rubygems'
37
+ require 'stubba'
38
+
39
+ class OrderTest < Test::Unit::TestCase
40
+
41
+ # illustrates stubbing instance method
42
+ def test_should_calculate_shipping_cost_based_on_total_weight
43
+ order = Order.new
44
+ order.stubs(:total_weight).returns(10)
45
+ assert_equal 60, order.shipping_cost
46
+ end
47
+
48
+ # illustrates stubbing class method
49
+ def test_should_count_number_of_orders_shipped_after_specified_date
50
+ now = Time.now; week_in_secs = 7 * 24 * 60 * 60
51
+ order_1 = Order.new; order_1.shipped_on = now - 1 * week_in_secs
52
+ order_2 = Order.new; order_2.shipped_on = now - 3 * week_in_secs
53
+ Order.stubs(:find_all).returns([order_1, order_2])
54
+ assert_equal 1, Order.number_shipped_since(now - 2 * week_in_secs)
55
+ end
56
+
57
+ # illustrates stubbing instance method for all instances of a class
58
+ def test_should_calculate_value_of_unshipped_orders
59
+ Order.stubs(:find_all).returns([Order.new, Order.new, Order.new])
60
+ Order.any_instance.stubs(:shipped_on).returns(nil)
61
+ Order.any_instance.stubs(:total_cost).returns(10)
62
+ assert_equal 30, Order.unshipped_value
63
+ end
64
+
65
+ end
@@ -1,7 +1,19 @@
1
- require 'smart_test_case'
2
- require 'mocha/auto_verify'
3
- require 'shared/backtracefilter'
1
+ require 'mocha_standalone'
2
+ require 'mocha/test_case_adapter'
3
+
4
+ require 'test/unit/testcase'
5
+
6
+ module Test
7
+
8
+ module Unit
9
+
10
+ class TestCase
11
+
12
+ include Mocha::Standalone
13
+ include Mocha::TestCaseAdapter
14
+
15
+ end
16
+
17
+ end
4
18
 
5
- class Test::Unit::TestCase
6
- include Mocha::AutoVerify unless ancestors.include?(Mocha::AutoVerify)
7
19
  end
@@ -1,6 +1,6 @@
1
- require 'stubba/class_method'
1
+ require 'mocha/class_method'
2
2
 
3
- module Stubba
3
+ module Mocha
4
4
 
5
5
  class AnyInstanceMethod < ClassMethod
6
6
 
@@ -6,12 +6,9 @@ require 'mocha/mock'
6
6
  #
7
7
  # See Mocha::MockMethods for methods on mock objects.
8
8
  module Mocha
9
+
9
10
  module AutoVerify
10
11
 
11
- def self.included(base) # :nodoc:
12
- base.add_teardown_method(:teardown_mocks)
13
- end
14
-
15
12
  def mocks # :nodoc:
16
13
  @mocks ||= []
17
14
  end
@@ -19,7 +16,7 @@ module Mocha
19
16
  def reset_mocks # :nodoc:
20
17
  @mocks = nil
21
18
  end
22
-
19
+
23
20
  # :call-seq: mock(name) -> mock object
24
21
  # mock(expected_methods = {}) -> mock object
25
22
  # mock(name, expected_methods = {}) -> mock object
@@ -83,9 +80,12 @@ module Mocha
83
80
  name, expectations = name_and_expectations_from_args(args)
84
81
  build_mock_with_expectations(:stub_everything, expectations, name)
85
82
  end
83
+
84
+ def verify_mocks # :nodoc:
85
+ mocks.each { |mock| mock.verify { yield if block_given? } }
86
+ end
86
87
 
87
88
  def teardown_mocks # :nodoc:
88
- mocks.each { |mock| mock.verify { add_assertion } }
89
89
  reset_mocks
90
90
  end
91
91
 
@@ -94,7 +94,7 @@ module Mocha
94
94
  expectation_type = :stubs if expectation_type == :stub_everything
95
95
  mock = Mocha::Mock.new(stub_everything, name)
96
96
  expectations.each do |method, result|
97
- mock.send(expectation_type, method).returns(result)
97
+ mock.__send__(expectation_type, method).returns(result)
98
98
  end
99
99
  mocks << mock
100
100
  mock
@@ -1,4 +1,4 @@
1
- module Stubba
1
+ module Mocha
2
2
 
3
3
  class Central
4
4
 
@@ -20,7 +20,7 @@ module Stubba
20
20
  end
21
21
 
22
22
  def unique_mocks
23
- stubba_methods.collect { |method| method.mock }.uniq
23
+ stubba_methods.inject({}) { |mocks, method| mocks[method.mock.__id__] = method.mock; mocks }.values
24
24
  end
25
25
 
26
26
  def unstub_all
@@ -1,6 +1,6 @@
1
1
  require 'mocha/metaclass'
2
2
 
3
- module Stubba
3
+ module Mocha
4
4
 
5
5
  class ClassMethod
6
6
 
@@ -26,29 +26,26 @@ module Stubba
26
26
  end
27
27
 
28
28
  def hide_original_method
29
- stubbee.metaclass.class_eval "alias_method :#{hidden_method}, :#{method}" if stubbee.metaclass.method_defined?(method)
29
+ stubbee.__metaclass__.class_eval "alias_method :#{hidden_method}, :#{method}" if stubbee.__metaclass__.method_defined?(method)
30
30
  end
31
31
 
32
32
  def define_new_method
33
- stubbee.metaclass.class_eval "def #{method}(*args, &block); mocha.method_missing(:#{method}, *args, &block); end"
33
+ stubbee.__metaclass__.class_eval "def #{method}(*args, &block); mocha.method_missing(:#{method}, *args, &block); end"
34
34
  end
35
35
 
36
36
  def remove_new_method
37
- stubbee.metaclass.class_eval "remove_method :#{method}"
37
+ stubbee.__metaclass__.class_eval "remove_method :#{method}"
38
38
  end
39
39
 
40
40
  def restore_original_method
41
- stubbee.metaclass.class_eval "alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}" if stubbee.metaclass.method_defined?(hidden_method)
41
+ stubbee.__metaclass__.class_eval "alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}" if stubbee.__metaclass__.method_defined?(hidden_method)
42
42
  end
43
43
 
44
44
  def hidden_method
45
- "__stubba__#{method.to_s.sub('!', '_exclamation_mark').sub('?', '_question_mark')}__stubba__"
45
+ method_name = method.to_s.gsub(/\W/) {|s| "_substituted_character_#{s[0]}_" }
46
+ "__stubba__#{method_name}__stubba__"
46
47
  end
47
48
 
48
- def cannot_replace_method_error
49
- Test::Unit::AssertionFailedError.new("Cannot replace #{method} because it is not defined in #{stubbee}.")
50
- end
51
-
52
49
  def eql?(other)
53
50
  return false unless (other.class == self.class)
54
51
  (stubbee == other.stubbee) and (method == other.method)
@@ -1,5 +1,12 @@
1
1
  require 'mocha/infinite_range'
2
2
  require 'mocha/pretty_parameters'
3
+ require 'mocha/expectation_error'
4
+
5
+ class Object
6
+
7
+ alias_method :__is_a__, :is_a?
8
+
9
+ end
3
10
 
4
11
  module Mocha
5
12
  # Methods on expectations returned from Mocha::MockMethods#expects and Mocha::MockMethods#stubs
@@ -13,19 +20,17 @@ module Mocha
13
20
  def ==(other)
14
21
  true
15
22
  end
16
- def to_s
17
- "** any **"
18
- end
19
23
  end
20
24
 
21
25
  attr_reader :method_name, :backtrace
22
26
 
23
- def initialize(method_name, backtrace = nil)
24
- @method_name = method_name
27
+ def initialize(mock, method_name, backtrace = nil)
28
+ @mock, @method_name = mock, method_name
25
29
  @count = 1
26
30
  @parameters, @parameter_block = AlwaysEqual.new, nil
27
31
  @invoked, @return_value = 0, nil
28
32
  @backtrace = backtrace || caller
33
+ @yield = nil
29
34
  end
30
35
 
31
36
  def yield?
@@ -79,11 +84,12 @@ module Mocha
79
84
  # object.expected_method # => verify succeeds
80
85
  def never
81
86
  times(0)
87
+ self
82
88
  end
83
89
 
84
- # :call-seq: at_least(minimum) -> expectation
90
+ # :call-seq: at_least(minimum_number_of_times) -> expectation
85
91
  #
86
- # Modifies expectation so that the expected method must be called at least a +minimum+ number of times.
92
+ # Modifies expectation so that the expected method must be called at least a +minimum_number_of_times+.
87
93
  # object = mock()
88
94
  # object.expects(:expected_method).at_least(2)
89
95
  # 3.times { object.expected_method } # => verify succeeds
@@ -91,8 +97,8 @@ module Mocha
91
97
  # object = mock()
92
98
  # object.expects(:expected_method).at_least(2)
93
99
  # object.expected_method # => verify fails
94
- def at_least(minimum)
95
- times(Range.at_least(minimum))
100
+ def at_least(minimum_number_of_times)
101
+ times(Range.at_least(minimum_number_of_times))
96
102
  self
97
103
  end
98
104
 
@@ -111,6 +117,36 @@ module Mocha
111
117
  self
112
118
  end
113
119
 
120
+ # :call-seq: at_most(maximum_number_of_times) -> expectation
121
+ #
122
+ # Modifies expectation so that the expected method must be called at most a +maximum_number_of_times+.
123
+ # object = mock()
124
+ # object.expects(:expected_method).at_most(2)
125
+ # 2.times { object.expected_method } # => verify succeeds
126
+ #
127
+ # object = mock()
128
+ # object.expects(:expected_method).at_most(2)
129
+ # 3.times { object.expected_method } # => verify fails
130
+ def at_most(maximum_number_of_times)
131
+ times(Range.at_most(maximum_number_of_times))
132
+ self
133
+ end
134
+
135
+ # :call-seq: at_most_once() -> expectation
136
+ #
137
+ # Modifies expectation so that the expected method must be called at most once.
138
+ # object = mock()
139
+ # object.expects(:expected_method).at_most_once
140
+ # object.expected_method # => verify succeeds
141
+ #
142
+ # object = mock()
143
+ # object.expects(:expected_method).at_most_once
144
+ # 2.times { object.expected_method } # => verify fails
145
+ def at_most_once()
146
+ at_most(1)
147
+ self
148
+ end
149
+
114
150
  # :call-seq: with(*arguments, &parameter_block) -> expectation
115
151
  #
116
152
  # Modifies expectation so that the expected method must be called with specified +arguments+.
@@ -151,19 +187,25 @@ module Mocha
151
187
  end
152
188
 
153
189
  # :call-seq: returns(value) -> expectation
190
+ # :call-seq: returns(*values) -> expectation
154
191
  #
155
192
  # Modifies expectation so that when the expected method is called, it returns the specified +value+.
156
193
  # object = mock()
157
- # object.expects(:expected_method).returns('result')
158
- # object.expected_method # => 'result'
194
+ # object.stubs(:stubbed_method).returns('result')
195
+ # object.stubbed_method # => 'result'
196
+ # object.stubbed_method # => 'result'
197
+ # If multiple +values+ are given, these are returned in turn on consecutive calls to the method.
198
+ # object = mock()
199
+ # object.stubs(:stubbed_method).returns(1, 2)
200
+ # object.stubbed_method # => 1
201
+ # object.stubbed_method # => 2
159
202
  # If +value+ is a Proc, then expected method will return result of calling Proc.
160
203
  # object = mock()
161
- # results = [111, 222]
162
- # object.stubs(:expected_method).returns(lambda { results.shift })
163
- # object.expected_method # => 111
164
- # object.expected_method # => 222
165
- def returns(value)
166
- @return_value = value
204
+ # object.stubs(:stubbed_method).returns(lambda { rand(100) })
205
+ # object.stubbed_method # => 41
206
+ # object.stubbed_method # => 77
207
+ def returns(*values)
208
+ @return_value = (values.size > 1) ? lambda { values.shift } : @return_value = values.first
167
209
  self
168
210
  end
169
211
 
@@ -174,7 +216,7 @@ module Mocha
174
216
  # object.expects(:expected_method).raises(Exception, 'message')
175
217
  # object.expected_method # => raises exception of class Exception and with message 'message'
176
218
  def raises(exception = RuntimeError, message = nil)
177
- @return_value = lambda{ raise exception, message }
219
+ @return_value = message ? lambda { raise exception, message } : lambda { raise exception }
178
220
  self
179
221
  end
180
222
 
@@ -183,22 +225,33 @@ module Mocha
183
225
  def invoke
184
226
  @invoked += 1
185
227
  yield(*@parameters_to_yield) if yield? and block_given?
186
- @return_value.is_a?(Proc) ? @return_value.call : @return_value
228
+ @return_value.__is_a__(Proc) ? @return_value.call : @return_value
187
229
  end
188
230
 
189
231
  def verify
190
232
  yield(self) if block_given?
191
233
  unless (@count === @invoked) then
192
- failure = Test::Unit::AssertionFailedError.new("#{message}: expected calls: #{@count}, actual calls: #{@invoked}")
193
- failure.set_backtrace(backtrace)
194
- raise failure
234
+ error = ExpectationError.new(error_message(@count, @invoked))
235
+ error.set_backtrace(filtered_backtrace)
236
+ raise error
195
237
  end
196
238
  end
239
+
240
+ def mocha_lib_directory
241
+ File.expand_path(File.join(File.dirname(__FILE__), "..")) + File::SEPARATOR
242
+ end
243
+
244
+ def filtered_backtrace
245
+ backtrace.reject { |location| Regexp.new(mocha_lib_directory).match(File.expand_path(location)) }
246
+ end
197
247
 
198
- def message
199
- params = @parameters.is_a?(Array) ? @parameters : [@parameters.to_s]
200
- params = PrettyParameters.new(params)
201
- ":#{@method_name}(#{params.pretty})"
248
+ def method_signature
249
+ return "#{method_name}" if @parameters.__is_a__(AlwaysEqual)
250
+ "#{@method_name}(#{PrettyParameters.new(@parameters).pretty})"
251
+ end
252
+
253
+ def error_message(expected_count, actual_count)
254
+ "#{@mock.mocha_inspect}.#{method_signature} - expected calls: #{expected_count}, actual calls: #{actual_count}"
202
255
  end
203
256
 
204
257
  # :startdoc:
@@ -217,20 +270,22 @@ module Mocha
217
270
 
218
271
  class MissingExpectation < Expectation
219
272
 
220
- def initialize(method_name, mock, expectations = [])
221
- super(method_name)
222
- @mock, @expectations = mock, expectations
273
+ def initialize(mock, method_name)
274
+ super
223
275
  @invoked = true
224
276
  end
225
277
 
226
278
  def verify
227
- msg = "Unexpected message #{message} sent to #{@mock.mocha_inspect}"
228
- msg << "\nSimilar expectations #{similar_expectations.collect { |expectation| expectation.message }.join("\n") }" unless similar_expectations.empty?
229
- raise Test::Unit::AssertionFailedError, msg if @invoked
279
+ msg = error_message(0, 1)
280
+ similar_expectations_list = similar_expectations.collect { |expectation| expectation.method_signature }.join("\n")
281
+ msg << "\nSimilar expectations:\n#{similar_expectations_list}" unless similar_expectations.empty?
282
+ error = ExpectationError.new(msg)
283
+ error.set_backtrace(filtered_backtrace)
284
+ raise error if @invoked
230
285
  end
231
286
 
232
287
  def similar_expectations
233
- @expectations.select { |expectation| expectation.method_name == self.method_name }
288
+ @mock.expectations.select { |expectation| expectation.method_name == self.method_name }
234
289
  end
235
290
 
236
291
  end