mocha 0.3.3 → 0.4.0

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