hardmock 1.2.3 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rake'
2
2
  require 'rubygems'
3
3
 
4
- HARDMOCK_VERSION = "1.2.3"
4
+ HARDMOCK_VERSION = "1.3.0"
5
5
 
6
6
  Dir["lib/tasks/*.rake"].each { |f| load f }
7
7
 
data/lib/assert_error.rb CHANGED
@@ -1,20 +1,22 @@
1
1
  require 'test/unit/assertions'
2
2
 
3
- module Test::Unit::Assertions
4
- # A better 'assert_raise'. +patterns+ can be one or more Regexps, or a literal String that
5
- # must match the entire error message.
6
- def assert_error(err_type,*patterns,&block)
7
- assert_not_nil block, "assert_error requires a block"
8
- assert((err_type and err_type.kind_of?(Class)), "First argument to assert_error has to be an error type")
9
- err = assert_raise(err_type) do
10
- block.call
11
- end
12
- patterns.each do |pattern|
13
- case pattern
14
- when Regexp
15
- assert_match(pattern, err.message)
16
- else
17
- assert_equal pattern, err.message
3
+ module Test::Unit #:nodoc:#
4
+ module Assertions #:nodoc:#
5
+ # A better 'assert_raise'. +patterns+ can be one or more Regexps, or a literal String that
6
+ # must match the entire error message.
7
+ def assert_error(err_type,*patterns,&block)
8
+ assert_not_nil block, "assert_error requires a block"
9
+ assert((err_type and err_type.kind_of?(Class)), "First argument to assert_error has to be an error type")
10
+ err = assert_raise(err_type) do
11
+ block.call
12
+ end
13
+ patterns.each do |pattern|
14
+ case pattern
15
+ when Regexp
16
+ assert_match(pattern, err.message)
17
+ else
18
+ assert_equal pattern, err.message
19
+ end
18
20
  end
19
21
  end
20
22
  end
@@ -7,6 +7,9 @@ module Hardmock
7
7
  # Raised for methods that should no longer be called. Hopefully, the exception message contains helpful alternatives.
8
8
  class DeprecationError < StandardError; end
9
9
 
10
+ # Raised when stubbing fails
11
+ class StubbingError < StandardError; end
12
+
10
13
  # Raised when it is discovered that an expected method call was never made.
11
14
  class VerifyError < StandardError
12
15
  def initialize(msg,unmet_expectations)
@@ -176,7 +176,7 @@ module Hardmock
176
176
  # Yield once
177
177
  @options[:block] = lambda do |block|
178
178
  if block.arity != 0 and block.arity != -1
179
- raise ExpectationError.new("Can't pass #{item.inspect} to block with arity #{block.arity} to <#{to_s}>")
179
+ raise ExpectationError.new("The given block was expected to have no parameter count; instead, got #{block.arity} to <#{to_s}>")
180
180
  end
181
181
  block.call
182
182
  end
@@ -6,8 +6,7 @@ module Hardmock
6
6
  attr_accessor :name
7
7
 
8
8
  def initialize
9
- @expectations = []
10
- @disappointed = false
9
+ clear_expectations
11
10
  end
12
11
 
13
12
  def happy?
@@ -19,6 +18,7 @@ module Hardmock
19
18
  end
20
19
 
21
20
  def add_expectation(expectation)
21
+ # puts "MockControl #{self.object_id.to_s(16)} adding expectation: #{expectation}"
22
22
  @expectations << expectation
23
23
  end
24
24
 
@@ -38,8 +38,16 @@ module Hardmock
38
38
  end
39
39
 
40
40
  def verify
41
+ # puts "MockControl #{self.object_id.to_s(16)} verify: happy? #{happy?}"
41
42
  @disappointed = !happy?
42
43
  raise VerifyError.new("Unmet expectations", @expectations) unless happy?
43
44
  end
45
+
46
+ def clear_expectations
47
+ @expectations = []
48
+ @disappointed = false
49
+ end
50
+
44
51
  end
52
+
45
53
  end
@@ -0,0 +1,174 @@
1
+ #
2
+ # Stubbing support
3
+ #
4
+ # Stubs methods on classes and instances
5
+ #
6
+
7
+ # Why's "metaid.rb":
8
+ class Object #:nodoc:#
9
+ # The hidden singleton lurks behind everyone
10
+ def metaclass #:nodoc:#
11
+ class << self
12
+ self
13
+ end
14
+ end
15
+
16
+ # Evaluate a block of code within the metaclass
17
+ def meta_eval(&blk) #:nodoc:#
18
+ metaclass.instance_eval(&blk)
19
+ end
20
+
21
+ # Adds methods to a metaclass
22
+ def meta_def(name, &blk) #:nodoc:#
23
+ meta_eval { define_method name, &blk }
24
+ end
25
+
26
+ # Defines an instance method within a class
27
+ # def class_def(name, &blk) #:nodoc:#
28
+ # class_eval { define_method name, &blk }
29
+ # end
30
+ end
31
+
32
+
33
+
34
+ module Hardmock
35
+
36
+ # == Hardmock: Stubbing and Mocking Concrete Methods
37
+ #
38
+ # Hardmock lets you stub and/or mock methods on concrete classes or objects.
39
+ #
40
+ # * To "stub" a concrete method is to rig it to return the same thing always, disregarding any arguments.
41
+ # * To "mock" a concrete method is to surplant its funcionality by delegating to a mock object who will cover this behavior.
42
+ #
43
+ # Mocked methods have their expectations considered along with all other mock object expectations.
44
+ #
45
+ # If you use stubbing or concrete mocking in the absence (or before creation) of other mocks, you need to invoke <tt>prepare_hardmock_control</tt>.
46
+ # Once <tt>verify_mocks</tt> or <tt>clear_expectaions</tt> is called, the overriden behavior in the target objects is restored.
47
+ #
48
+ # == Examples
49
+ #
50
+ # River.stubs!(:sounds_like).returns("gurgle")
51
+ #
52
+ # River.expects!(:jump).returns("splash")
53
+ #
54
+ # rogue.stubs!(:sounds_like).returns("pshshsh")
55
+ #
56
+ # rogue.expects!(:rawhide_tanning_solvents).returns("giant snapping turtles")
57
+ #
58
+ module Stubbing
59
+ # Exists only for documentation
60
+ end
61
+
62
+ class StubbedMethod #:nodoc:#
63
+ attr_reader :target, :method_name
64
+
65
+ def initialize(target, method_name)
66
+ @target = target
67
+ @method_name = method_name
68
+
69
+ Hardmock.add_stubbed_method self
70
+ end
71
+
72
+ def invoke(args)
73
+ @return_value
74
+ end
75
+
76
+ def returns(stubbed_return)
77
+ @return_value = stubbed_return
78
+ end
79
+ end
80
+
81
+ class MockedMethod < StubbedMethod #:nodoc:#
82
+
83
+ def initialize(target, method_name, mock)
84
+ super target,method_name
85
+ @mock = mock
86
+ end
87
+
88
+ def invoke(args)
89
+ @mock.__send__(self.method_name.to_sym, *args)
90
+ end
91
+
92
+ end
93
+
94
+ class ::Object
95
+ def stubs!(method_name)
96
+ _ensure_stubbable method_name
97
+
98
+ method_name = method_name.to_s
99
+ stubbed_method = Hardmock::StubbedMethod.new(self, method_name)
100
+
101
+ meta_eval do
102
+ alias_method "_hardmock_original_#{method_name}".to_sym, method_name.to_sym
103
+ end
104
+
105
+ meta_def method_name do |*args|
106
+ stubbed_method.invoke(args)
107
+ end
108
+
109
+ stubbed_method
110
+ end
111
+
112
+ def expects!(method_name, *args, &block)
113
+ _ensure_stubbable method_name
114
+
115
+ method_name = method_name.to_s
116
+
117
+ if @_my_mock.nil?
118
+ @_my_mock = Mock.new(_my_name, $main_mock_control)
119
+ stubbed_method = Hardmock::MockedMethod.new(self, method_name, @_my_mock)
120
+ meta_eval do
121
+ alias_method "_hardmock_original_#{method_name}".to_sym, method_name.to_sym
122
+ end
123
+ meta_def(method_name) do |*args|
124
+ stubbed_method.invoke(args)
125
+ end
126
+ end
127
+
128
+ return @_my_mock.expects(method_name, *args, &block)
129
+ end
130
+
131
+ def _ensure_stubbable(method_name)
132
+ unless self.respond_to?(method_name.to_sym)
133
+ msg = "Cannot stub non-existant "
134
+ if self.kind_of?(Class)
135
+ msg += "class method #{_my_name}."
136
+ else
137
+ msg += "method #{_my_name}#"
138
+ end
139
+ msg += method_name.to_s
140
+ raise Hardmock::StubbingError.new(msg)
141
+ end
142
+ end
143
+
144
+ def _my_name
145
+ self.kind_of?(Class) ? self.name : self.class.name
146
+ end
147
+
148
+ def _clear_mock
149
+ @_my_mock = nil
150
+ end
151
+
152
+ end
153
+
154
+ class << self
155
+ def add_stubbed_method(stubbed_method)
156
+ all_stubbed_methods << stubbed_method
157
+ end
158
+
159
+ def all_stubbed_methods
160
+ $all_stubbed_methods ||= []
161
+ end
162
+
163
+ def restore_all_stubbed_methods
164
+ all_stubbed_methods.each do |sm|
165
+ sm.target.meta_eval do
166
+ alias_method sm.method_name.to_sym, "_hardmock_original_#{sm.method_name}".to_sym
167
+ end
168
+ sm.target._clear_mock
169
+ end
170
+ end
171
+ end
172
+
173
+ end
174
+
data/lib/hardmock.rb CHANGED
@@ -7,6 +7,7 @@ require 'hardmock/trapper'
7
7
  require 'hardmock/expector'
8
8
  require 'hardmock/expectation'
9
9
  require 'hardmock/expectation_builder'
10
+ require 'hardmock/stubbing'
10
11
 
11
12
  module Hardmock
12
13
 
@@ -73,7 +74,7 @@ module Hardmock
73
74
  # For more info on how to use your mocks, see Mock and Expectation
74
75
  #
75
76
  def create_mocks(*mock_names)
76
- @main_mock_control ||= MockControl.new
77
+ prepare_hardmock_control unless @main_mock_control
77
78
 
78
79
  mocks = {}
79
80
  mock_names.each do |mock_name|
@@ -89,6 +90,15 @@ module Hardmock
89
90
  return mocks.clone
90
91
  end
91
92
 
93
+ def prepare_hardmock_control
94
+ if @main_mock_control.nil?
95
+ @main_mock_control = MockControl.new
96
+ $main_mock_control = @main_mock_control
97
+ else
98
+ raise "@main_mock_control is already setup for this test!"
99
+ end
100
+ end
101
+
92
102
  alias :create_mock :create_mocks
93
103
 
94
104
  # Ensures that all expectations have been met. If not, VerifyException is
@@ -101,17 +111,34 @@ module Hardmock
101
111
  return unless @main_mock_control
102
112
  return if @main_mock_control.disappointed? and !force
103
113
  @main_mock_control.verify
114
+ ensure
115
+ @main_mock_control.clear_expectations if @main_mock_control
116
+ Hardmock.restore_all_stubbed_methods
117
+ $main_mock_control = nil
104
118
  end
105
119
 
120
+ # Purge the main MockControl of all expectations, restore all concrete stubbed/mocked methods
121
+ def clear_expectations
122
+ @main_mock_control.clear_expectations if @main_mock_control
123
+ Hardmock.restore_all_stubbed_methods
124
+ $main_mock_control = nil
125
+ end
106
126
 
127
+ # def self.set_main_mock_control(control)
128
+ # $main_mock_control = control
129
+ # end
107
130
 
131
+ # def self.main_mock_control
132
+ # raise "No main mock control set yet... chickening out" if $main_mock_control.nil?
133
+ # $main_mock_control
134
+ # end
108
135
 
109
136
  end
110
137
 
111
138
  # Insert Hardmock functionality into the TestCase base class
112
139
  require 'test/unit/testcase'
113
140
  unless Test::Unit::TestCase.instance_methods.include?('hardmock_teardown')
114
- class Test::Unit::TestCase
141
+ class Test::Unit::TestCase #:nodoc:#
115
142
  include Hardmock
116
143
  end
117
144
  end
@@ -3,13 +3,13 @@ require 'assert_error'
3
3
 
4
4
  class AssertErrorTest < Test::Unit::TestCase
5
5
 
6
- def test_assert_error
6
+ it "specfies an error type and message that should be raised" do
7
7
  assert_error RuntimeError, "Too funky" do
8
8
  raise RuntimeError.new("Too funky")
9
9
  end
10
10
  end
11
11
 
12
- def test_assert_error_message_doesnt_match
12
+ it "flunks if the error message is wrong" do
13
13
  err = assert_raise Test::Unit::AssertionFailedError do
14
14
  assert_error RuntimeError, "not good" do
15
15
  raise RuntimeError.new("Too funky")
@@ -19,7 +19,7 @@ class AssertErrorTest < Test::Unit::TestCase
19
19
  assert_match(/too funky/i, err.message)
20
20
  end
21
21
 
22
- def test_assert_error_type_doesnt_match
22
+ it "flunks if the error type is wrong" do
23
23
  err = assert_raise Test::Unit::AssertionFailedError do
24
24
  assert_error StandardError, "Too funky" do
25
25
  raise RuntimeError.new("Too funky")
@@ -29,13 +29,13 @@ class AssertErrorTest < Test::Unit::TestCase
29
29
  assert_match(/RuntimeError/i, err.message)
30
30
  end
31
31
 
32
- def test_assert_error_regexp_matching
32
+ it "can match error message text using a series of Regexps" do
33
33
  assert_error StandardError, /too/i, /funky/i do
34
34
  raise StandardError.new("Too funky")
35
35
  end
36
36
  end
37
37
 
38
- def test_assert_error_regexp_matching_fails
38
+ it "flunks if the error message doesn't match all the Regexps" do
39
39
  err = assert_raise Test::Unit::AssertionFailedError do
40
40
  assert_error StandardError, /way/i, /too/i, /funky/i do
41
41
  raise StandardError.new("Too funky")
@@ -44,7 +44,7 @@ class AssertErrorTest < Test::Unit::TestCase
44
44
  assert_match(/way/i, err.message)
45
45
  end
46
46
 
47
- def test_assert_error_no_matchine
47
+ it "can operate without any message specification" do
48
48
  assert_error StandardError do
49
49
  raise StandardError.new("ooof")
50
50
  end
@@ -15,11 +15,11 @@ class AutoVerifyTest < Test::Unit::TestCase
15
15
  # TESTS
16
16
  #
17
17
 
18
- def test_should_auto_verify_mocks_in_teardown
18
+ it "auto-verifies all mocks in teardown" do
19
19
  write_and_execute_test
20
20
  end
21
21
 
22
- def test_should_auto_verify_mocks_even_if_user_has_own_teardown
22
+ it "auto-verifies even if user defines own teardown" do
23
23
  @teardown_code =<<-EOM
24
24
  def teardown
25
25
  # just in the way
@@ -28,7 +28,7 @@ class AutoVerifyTest < Test::Unit::TestCase
28
28
  write_and_execute_test
29
29
  end
30
30
 
31
- def test_should_auto_verify_mocks_but_not_obscure_natural_failures
31
+ should "not obscure normal failures when verification fails" do
32
32
  @test_code =<<-EOM
33
33
  def test_setup_doomed_expectation
34
34
  create_mock :automobile
@@ -40,7 +40,7 @@ class AutoVerifyTest < Test::Unit::TestCase
40
40
  write_and_execute_test
41
41
  end
42
42
 
43
- def test_should_not_prevent_user_teardown_even_if_verification_fails
43
+ should "not skip user-defined teardown when verification fails" do
44
44
  @teardown_code =<<-EOM
45
45
  def teardown
46
46
  puts "User teardown"
@@ -50,7 +50,8 @@ class AutoVerifyTest < Test::Unit::TestCase
50
50
  assert_output_contains(/User teardown/)
51
51
  end
52
52
 
53
- def test_should_not_raise_error_if_verification_goes_according_to_plan
53
+ it "is quiet when verification is ok" do
54
+ # def test_should_not_raise_error_if_verification_goes_according_to_plan
54
55
  @test_code =<<-EOM
55
56
  def test_ok
56
57
  create_mock :automobile
@@ -70,7 +71,8 @@ class AutoVerifyTest < Test::Unit::TestCase
70
71
  assert_output_contains(/User teardown/)
71
72
  end
72
73
 
73
- def test_should_not_do_verification_if_user_teardown_explodes
74
+ should "not auto-verify if user teardown explodes" do
75
+ # def test_should_not_do_verification_if_user_teardown_explodes
74
76
  @teardown_code =<<-EOM
75
77
  def teardown
76
78
  raise "self destruct"
@@ -81,7 +83,7 @@ class AutoVerifyTest < Test::Unit::TestCase
81
83
  assert_output_contains(/self destruct/)
82
84
  end
83
85
 
84
- def test_should_not_obscure_inherited_teardown
86
+ it "plays nice with inherited teardown methods" do
85
87
  @full_code ||=<<-EOTEST
86
88
  require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
87
89
  require 'hardmock'
@@ -101,7 +103,7 @@ class AutoVerifyTest < Test::Unit::TestCase
101
103
  assert_output_contains(/Test helper teardown/)
102
104
  end
103
105
 
104
- def test_should_simultaneously_support_inherited_and_user_and_hardmock_teardown
106
+ it "plays nice with inherited and user-defined teardowns at the same time" do
105
107
  @full_code ||=<<-EOTEST
106
108
  require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
107
109
  class Test::Unit::TestCase
@@ -135,9 +137,6 @@ class AutoVerifyTest < Test::Unit::TestCase
135
137
  def run_test(tbody)
136
138
  File.open(temp_test_file,"w") { |f| f.print(tbody) }
137
139
  @test_output = `ruby #{temp_test_file} 2>&1`
138
- # puts "------------------------"
139
- # puts @test_output
140
- # puts "------------------------"
141
140
  end
142
141
 
143
142
  def remove_temp_test_file
@@ -14,7 +14,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
14
14
  # TESTS
15
15
  #
16
16
 
17
- def test_verify_should_raise_verify_error_if_expected_method_not_called
17
+ it "raises VerifyError if expected method not called" do
18
18
  @bird.expects.flap_flap
19
19
 
20
20
  err = assert_raise VerifyError do
@@ -23,7 +23,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
23
23
  assert_match(/unmet expectations/i, err.message)
24
24
  end
25
25
 
26
- def test_verify_should_not_raise_when_expected_calls_made_in_order
26
+ should "not raise when expected calls are made in order" do
27
27
  @bird.expects.flap_flap
28
28
  @bird.expects.bang
29
29
  @bird.expects.plop
@@ -35,7 +35,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
35
35
  @bird._verify
36
36
  end
37
37
 
38
- def test_should_raise_expectation_error_when_unexpected_method_called
38
+ it "raises ExpectationError when unexpected method are called" do
39
39
  @bird.expects.flap_flap
40
40
 
41
41
  err = assert_raise ExpectationError do
@@ -44,7 +44,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
44
44
  assert_match(/wrong method/i, err.message)
45
45
  end
46
46
 
47
- def test_bad_argument_call
47
+ it "raises ExpectationError on bad arguments" do
48
48
  @bird.expects.flap_flap(:swoosh)
49
49
 
50
50
  err = assert_raise ExpectationError do
@@ -53,7 +53,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
53
53
  assert_match(/wrong arguments/i, err.message)
54
54
  end
55
55
 
56
- def test_verify_should_raise_verify_error_when_not_all_expected_methods_called
56
+ it "raises VerifyError when not all expected methods are called" do
57
57
  @bird.expects.flap_flap
58
58
  @bird.expects.bang
59
59
  @bird.expects.plop
@@ -66,7 +66,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
66
66
  assert_match(/unmet expectations/i, err.message)
67
67
  end
68
68
 
69
- def test_should_raise_expectation_error_when_calls_made_out_of_order
69
+ it "raises ExpectationError when calls are made out of order" do
70
70
  @bird.expects.flap_flap
71
71
  @bird.expects.bang
72
72
  @bird.expects.plop
@@ -78,7 +78,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
78
78
  assert_match(/wrong method/i, err.message)
79
79
  end
80
80
 
81
- def test_should_return_given_value_when_specified
81
+ it "returns the configured value" do
82
82
  @bird.expects.plop.returns(':P')
83
83
  assert_equal ':P', @bird.plop
84
84
  @bird._verify
@@ -88,13 +88,13 @@ class DirectMockUsageTest < Test::Unit::TestCase
88
88
  @bird._verify
89
89
  end
90
90
 
91
- def test_should_return_nil_value_when_none_specified
91
+ it "returns nil when no return is specified" do
92
92
  @bird.expects.plop
93
93
  assert_nil @bird.plop
94
94
  @bird._verify
95
95
  end
96
96
 
97
- def test_raise_should_raise_given_exception_when_specified
97
+ it "raises the configured exception" do
98
98
  err = RuntimeError.new('shaq')
99
99
  @bird.expects.plop.raises(err)
100
100
  actual_err = assert_raise RuntimeError do
@@ -104,7 +104,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
104
104
  @bird._verify
105
105
  end
106
106
 
107
- def test_raise_should_raise_given_string_wrapped_in_runtime_error
107
+ it "raises a RuntimeError when told to 'raise' a string" do
108
108
  @bird.expects.plop.raises('shaq')
109
109
  err = assert_raise RuntimeError do
110
110
  @bird.plop
@@ -113,7 +113,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
113
113
  @bird._verify
114
114
  end
115
115
 
116
- def test_raise_should_raise_a_canned_runtime_error_if_nothing_given
116
+ it "raises a default RuntimeError" do
117
117
  @bird.expects.plop.raises
118
118
  err = assert_raise RuntimeError do
119
119
  @bird.plop
@@ -122,14 +122,14 @@ class DirectMockUsageTest < Test::Unit::TestCase
122
122
  @bird._verify
123
123
  end
124
124
 
125
- def test_should_be_happy_with_correct_expected_arguments
125
+ it "is quiet when correct arguments given" do
126
126
  thing = Object.new
127
127
  @bird.expects.plop(:big,'one',thing)
128
128
  @bird.plop(:big,'one',thing)
129
129
  @bird._verify
130
130
  end
131
131
 
132
- def test_should_raise_expectation_error_when_wrong_number_of_arguemnts_specified
132
+ it "raises ExpectationError when wrong number of arguments specified" do
133
133
  thing = Object.new
134
134
  @bird.expects.plop(:big,'one',thing)
135
135
  err = assert_raise ExpectationError do
@@ -156,7 +156,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
156
156
  @bird._verify
157
157
  end
158
158
 
159
- def test_should_raise_expectation_error_when_arguemnts_dont_match
159
+ it "raises ExpectationError when arguments don't match" do
160
160
  thing = Object.new
161
161
  @bird.expects.plop(:big,'one',thing)
162
162
  err = assert_raise ExpectationError do
@@ -166,7 +166,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
166
166
  @bird._verify
167
167
  end
168
168
 
169
- def test_should_yield_to_block_given
169
+ it "can use a block for custom reactions" do
170
170
  mitt = nil
171
171
  @bird.expects.plop { mitt = :ball }
172
172
  assert_nil mitt
@@ -182,7 +182,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
182
182
  @bird._verify
183
183
  end
184
184
 
185
- def test_shouldnt_care_about_arguments_if_block_given
185
+ it "passes mock-call arguments to the expectation block" do
186
186
  ball = nil
187
187
  mitt = nil
188
188
  @bird.expects.plop {|arg1,arg2|
@@ -197,7 +197,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
197
197
  @bird._verify
198
198
  end
199
199
 
200
- def test_should_check_arguments_if_specified_when_block_given
200
+ it "validates arguments if specified in addition to a block" do
201
201
  ball = nil
202
202
  mitt = nil
203
203
  @bird.expects.plop(:ball,:mitt) {|arg1,arg2|
@@ -244,7 +244,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
244
244
  @bird._verify
245
245
  end
246
246
 
247
- def test_runtime_blocks_get_passed_to_expectation_blocks
247
+ it "passes runtime blocks to the expectation block as the final argument" do
248
248
  runtime_block_called = false
249
249
  got_arg = nil
250
250
 
@@ -271,7 +271,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
271
271
  @bird._verify
272
272
  end
273
273
 
274
- def test_runtime_blocks_get_passed_to_expectation_blocks__no_arguments
274
+ it "passes the runtime block to the expectation block as sole argument if no other args come into play" do
275
275
  runtime_block_called = false
276
276
  @bird.expects.subscribe { |block| block.call }
277
277
  @bird.subscribe do
@@ -280,7 +280,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
280
280
  assert runtime_block_called, "The runtime block should have been invoked by the user block"
281
281
  end
282
282
 
283
- def test_expect_runtime_block_but_none_sent
283
+ it "provides nil as final argument if expectation block seems to want a block" do
284
284
  invoked = false
285
285
  @bird.expects.kablam(:scatter) { |shot,block|
286
286
  assert_equal :scatter, shot, "Wrong shot"
@@ -293,7 +293,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
293
293
  @bird._verify
294
294
  end
295
295
 
296
- def test_can_set_return_after_blocks
296
+ it "can set explicit return after an expectation block" do
297
297
  got = nil
298
298
  @bird.expects.kablam(:scatter) { |shot|
299
299
  got = shot
@@ -305,7 +305,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
305
305
  @bird._verify
306
306
  end
307
307
 
308
- def test_can_set_raises_after_blocks
308
+ it "can raise after an expectation block" do
309
309
  got = nil
310
310
  @bird.expects.kablam(:scatter) do |shot|
311
311
  got = shot
@@ -319,7 +319,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
319
319
  @bird._verify
320
320
  end
321
321
 
322
- def test_expectation_block_value_is_captured
322
+ it "stores the semantic value of the expectation block after it executes" do
323
323
  expectation = @bird.expects.kablam(:slug) { |shot|
324
324
  "The shot was #{shot}"
325
325
  }
@@ -336,7 +336,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
336
336
  end
337
337
 
338
338
 
339
- def test_expectation_block_value_is_used_for_return_value
339
+ it "uses the value of the expectation block as the default return value" do
340
340
  @bird.expects.kablam(:scatter) { |shot|
341
341
  "The shot was #{shot}"
342
342
  }
@@ -345,7 +345,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
345
345
  @bird._verify
346
346
  end
347
347
 
348
- def test_expectation_is_still_returned_when_using_returns
348
+ it "returns the Expectation even if 'returns' is used" do
349
349
  expectation = @bird.expects.kablam(:slug) { |shot|
350
350
  "The shot was #{shot}"
351
351
  }.returns :hosed
@@ -361,7 +361,7 @@ class DirectMockUsageTest < Test::Unit::TestCase
361
361
  @bird._verify
362
362
  end
363
363
 
364
- def test_expectation_is_still_returned_when_using_raises
364
+ it "returns the Expectation even if 'raises' is used" do
365
365
  expectation = @bird.expects.kablam(:slug) { |shot|
366
366
  "The shot was #{shot}"
367
367
  }.raises "aiee!"
@@ -378,13 +378,13 @@ class DirectMockUsageTest < Test::Unit::TestCase
378
378
  end
379
379
 
380
380
 
381
- def test_expect_assignment
381
+ it "supports assignment-style methods" do
382
382
  @bird.expects.size = "large"
383
383
  @bird.size = "large"
384
384
  @bird._verify
385
385
  end
386
386
 
387
- def test_expect_assignment_with_raise
387
+ it "supports assignments and raising (using explicit-method syntax)" do
388
388
  @bird.expects('size=','large').raises "boom"
389
389
 
390
390
  err = assert_raise RuntimeError do
@@ -4,33 +4,11 @@ require 'assert_error'
4
4
 
5
5
  class HardmockTest < Test::Unit::TestCase
6
6
 
7
- def setup
8
- end
9
-
10
- def teardown
11
- end
12
-
13
- #
14
- # HELPERS
15
- #
16
-
17
- def assert_mock_exists(name)
18
- assert_not_nil @all_mocks, "@all_mocks not here yet"
19
- mo = @all_mocks[name]
20
- assert_not_nil mo, "Mock '#{name}' not in @all_mocks"
21
- assert_kind_of Mock, mo, "Wrong type of object, wanted a Mock"
22
- assert_equal name.to_s, mo._name, "Mock '#{name}' had wrong name"
23
- ivar = self.instance_variable_get("@#{name}")
24
- assert_not_nil ivar, "Mock '#{name}' not set as ivar"
25
- assert_same mo, ivar, "Mock '#{name}' ivar not same as instance in @all_mocks"
26
- assert_same @main_mock_control, mo._control, "Mock '#{name}' doesn't share the main mock control"
27
- end
28
-
29
7
  #
30
8
  # TESTS
31
9
  #
32
10
 
33
- def test_create_mock_and_create_mocks
11
+ it "conveniently creates mocks using create_mock and create_mocks" do
34
12
  assert_nil @main_mock_control, "@main_mock_control not expected yet"
35
13
 
36
14
  h = create_mock :donkey
@@ -54,7 +32,7 @@ class HardmockTest < Test::Unit::TestCase
54
32
  assert_mock_exists :donkey
55
33
  end
56
34
 
57
- def test_expect
35
+ it "provides literal 'expects' syntax" do
58
36
  assert_nil @order, "Should be no @order yet"
59
37
  create_mock :order
60
38
  assert_not_nil @order, "@order should be built"
@@ -72,7 +50,7 @@ class HardmockTest < Test::Unit::TestCase
72
50
  verify_mocks
73
51
  end
74
52
 
75
- def test_typical_multi_mock_use
53
+ it "supports several mocks at once" do
76
54
  create_mocks :order_builder, :order, :customer
77
55
 
78
56
  @order_builder.expects.create_new_order.returns @order
@@ -88,7 +66,7 @@ class HardmockTest < Test::Unit::TestCase
88
66
  verify_mocks
89
67
  end
90
68
 
91
- def test_typical_multi_mock_use_out_of_order
69
+ it "enforces inter-mock call ordering" do
92
70
  create_mocks :order_builder, :order, :customer
93
71
 
94
72
  @order_builder.expects.create_new_order.returns @order
@@ -108,10 +86,6 @@ class HardmockTest < Test::Unit::TestCase
108
86
  assert_error VerifyError, /unmet expectations/i do
109
87
  verify_mocks
110
88
  end
111
-
112
- # Appease the verifier
113
- @order.account_no = 1234
114
- @order.save!
115
89
  end
116
90
 
117
91
  class UserPresenter
@@ -127,7 +101,7 @@ class HardmockTest < Test::Unit::TestCase
127
101
  end
128
102
  end
129
103
 
130
- def test_mvp_usage_pattern
104
+ it "makes MVP testing simple" do
131
105
  mox = create_mocks :model, :view
132
106
 
133
107
  data_change = @model.expects.when(:data_changes) { |evt,block| block }
@@ -150,7 +124,7 @@ class HardmockTest < Test::Unit::TestCase
150
124
  verify_mocks
151
125
  end
152
126
 
153
- def test_verify_mocks_repeated_anger
127
+ it "continues to function after verify, if verification error is controlled" do
154
128
  mox = create_mocks :model, :view
155
129
  data_change = @model.expects.when(:data_changes) { |evt,block| block }
156
130
  user_edit = @view.expects.when(:user_edited) { |evt,block| block }
@@ -174,18 +148,11 @@ class HardmockTest < Test::Unit::TestCase
174
148
  verify_mocks(false)
175
149
  end
176
150
 
177
- # Finish meeting expectations and see good verification behavior
178
- @view.user_name = "Da Croz"
179
- verify_mocks
180
-
181
151
  @model.expects.never_gonna_happen
182
152
 
183
153
  assert_error VerifyError, /unmet expectations/i, /never_gonna_happen/i do
184
154
  verify_mocks
185
155
  end
186
-
187
- # Appease the verifier
188
- @model.never_gonna_happen
189
156
  end
190
157
 
191
158
  class UserPresenterBroken
@@ -199,7 +166,7 @@ class HardmockTest < Test::Unit::TestCase
199
166
  end
200
167
  end
201
168
 
202
- def test_mvp_usage_with_failures_in_constructor
169
+ it "flunks for typical Presenter constructor wiring failure" do
203
170
  mox = create_mocks :model, :view
204
171
 
205
172
  data_change = @model.expects.when(:data_changes) { |evt,block| block }
@@ -213,16 +180,9 @@ class HardmockTest < Test::Unit::TestCase
213
180
  assert_match(/unmet expectations/i, err.message)
214
181
  assert_match(/view.when\(:user_edited\)/i, err.message)
215
182
 
216
- assert_error VerifyError, /unmet expectations/i do
217
- verify_mocks
218
- end
219
-
220
- # Appease the verifier
221
- @view.when(:user_edited)
222
-
223
183
  end
224
184
 
225
- def test_mvp_usage_pattern_with_convenience_trap
185
+ it "provides convenient event-subscription trap syntax for MVP testing" do
226
186
  mox = create_mocks :model, :view
227
187
 
228
188
  data_change = @model.trap.when(:data_changes)
@@ -245,6 +205,13 @@ class HardmockTest < Test::Unit::TestCase
245
205
  verify_mocks
246
206
  end
247
207
 
208
+ it "raises if you try to pass an expectation block to 'trap'" do
209
+ create_mock :model
210
+ assert_error Hardmock::ExpectationError, /blocks/i, /trap/i do
211
+ @model.trap.when(:some_event) do raise "huh?" end
212
+ end
213
+ end
214
+
248
215
  class Grinder
249
216
  def initialize(objects)
250
217
  @chute = objects[:chute]
@@ -259,7 +226,7 @@ class HardmockTest < Test::Unit::TestCase
259
226
  end
260
227
  end
261
228
 
262
- def test_internal_iteration_usage
229
+ it "lets you write clear iteration-oriented expectations" do
263
230
  grinder = Grinder.new create_mocks(:blade, :chute, :bucket)
264
231
 
265
232
  # Style 1: assertions on method args is done explicitly in block
@@ -290,7 +257,7 @@ class HardmockTest < Test::Unit::TestCase
290
257
  verify_mocks
291
258
  end
292
259
 
293
- def test_internal_iteration_using_yield
260
+ it "further supports iteration testing using 'yield'" do
294
261
  grinder = Grinder.new create_mocks(:blade, :chute, :bucket)
295
262
 
296
263
  @chute.expects.each_bean(:side_slot).yields :bean1, :bean2
@@ -322,7 +289,7 @@ class HardmockTest < Test::Unit::TestCase
322
289
  end
323
290
  end
324
291
 
325
- def test_internal_locking_scenario
292
+ it "makes mutex-style locking scenarios easy to test" do
326
293
  hurt = HurtLocker.new create_mocks(:locker, :store)
327
294
 
328
295
  @locker.expects.with_lock(:main).yields
@@ -333,7 +300,7 @@ class HardmockTest < Test::Unit::TestCase
333
300
  verify_mocks
334
301
  end
335
302
 
336
- def test_internal_locking_scenario_with_inner_error
303
+ it "makes it easy to simulate error in mutex-style locking scenarios" do
337
304
  hurt = HurtLocker.new create_mocks(:locker, :store)
338
305
  err = StandardError.new('fmshooop')
339
306
  @locker.expects.with_lock(:main).yields
@@ -345,7 +312,7 @@ class HardmockTest < Test::Unit::TestCase
345
312
  verify_mocks
346
313
  end
347
314
 
348
- def test_returning_false_actually_returns_false_and_not_nil
315
+ it "actually returns 'false' instead of nil when mocking boolean return values" do
349
316
  create_mock :car
350
317
  @car.expects.ignition_on?.returns(true)
351
318
  assert_equal true, @car.ignition_on?, "Should be true"
@@ -353,7 +320,7 @@ class HardmockTest < Test::Unit::TestCase
353
320
  assert_equal false, @car.ignition_on?, "Should be false"
354
321
  end
355
322
 
356
- def test_should_be_able_to_mock_some_methods_inherited_from_object
323
+ it "can mock most methods inherited from object using literal syntax" do
357
324
  target_methods = %w|id clone display dup eql? ==|
358
325
  create_mock :foo
359
326
  target_methods.each do |m|
@@ -362,14 +329,14 @@ class HardmockTest < Test::Unit::TestCase
362
329
  end
363
330
  end
364
331
 
365
- def test_should_support_expect_as_an_alias_for_expects
332
+ it "provides 'expect' as an alias for 'expects'" do
366
333
  create_mock :foo
367
334
  @foo.expect.boomboom
368
335
  @foo.boomboom
369
336
  verify_mocks
370
337
  end
371
338
 
372
- def test_should_not_raise_expectation_errors_for_some_methods_defined_on_object
339
+ it "does not interfere with a core subset of Object methods" do
373
340
  create_mock :foo
374
341
  @foo.method(:inspect)
375
342
  @foo.inspect
@@ -379,7 +346,7 @@ class HardmockTest < Test::Unit::TestCase
379
346
  verify_mocks
380
347
  end
381
348
 
382
- def test_should_support_raising_errors_within_expectation_blocks
349
+ it "can raise errors from within an expectation block" do
383
350
  create_mock :cat
384
351
  @cat.expects.meow do |arg|
385
352
  assert_equal "mix", arg
@@ -390,7 +357,7 @@ class HardmockTest < Test::Unit::TestCase
390
357
  end
391
358
  end
392
359
 
393
- def test_should_support_raising_errors_after_expectation_blocks
360
+ it "can raise errors AFTER an expectation block" do
394
361
  create_mock :cat
395
362
  @cat.expects.meow do |arg|
396
363
  assert_equal "mix", arg
@@ -400,7 +367,7 @@ class HardmockTest < Test::Unit::TestCase
400
367
  end
401
368
  end
402
369
 
403
- def test_should_raise_when_create_mocks_is_given_a_nil_name
370
+ it "raises an immediate error if a mock is created with a nil name (common mistake: create_mock @cat)" do
404
371
  # I make this mistake all the time: Typing in an instance var name instead of a symbol in create_mocks.
405
372
  # When you do that, you're effectively passing nil(s) in as mock names.
406
373
  assert_error ArgumentError, /'nil' is not a valid name for a mock/ do
@@ -408,10 +375,33 @@ class HardmockTest < Test::Unit::TestCase
408
375
  end
409
376
  end
410
377
 
411
- def test_should_display_mock_name_in_inspect_output
378
+ it "overrides 'inspect' to make nice output" do
412
379
  create_mock :hay_bailer
413
380
  assert_equal "<Mock hay_bailer>", @hay_bailer.inspect, "Wrong output from 'inspect'"
414
381
  end
415
382
 
383
+ it "raises is prepare_hardmock_control is invoked after create_mocks, or more than once" do
384
+ create_mock :hi_there
385
+ create_mocks :another, :one
386
+ assert_error RuntimeError, /already setup/ do
387
+ prepare_hardmock_control
388
+ end
389
+ end
390
+
391
+ #
392
+ # HELPERS
393
+ #
394
+
395
+ def assert_mock_exists(name)
396
+ assert_not_nil @all_mocks, "@all_mocks not here yet"
397
+ mo = @all_mocks[name]
398
+ assert_not_nil mo, "Mock '#{name}' not in @all_mocks"
399
+ assert_kind_of Mock, mo, "Wrong type of object, wanted a Mock"
400
+ assert_equal name.to_s, mo._name, "Mock '#{name}' had wrong name"
401
+ ivar = self.instance_variable_get("@#{name}")
402
+ assert_not_nil ivar, "Mock '#{name}' not set as ivar"
403
+ assert_same mo, ivar, "Mock '#{name}' ivar not same as instance in @all_mocks"
404
+ assert_same @main_mock_control, mo._control, "Mock '#{name}' doesn't share the main mock control"
405
+ end
416
406
  end
417
407
 
@@ -0,0 +1,274 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
+ require 'hardmock'
3
+ require 'assert_error'
4
+
5
+ class StubbingTest < Test::Unit::TestCase
6
+
7
+ #
8
+ # TESTS
9
+ #
10
+
11
+ it "stubs a class method (and un-stubs after verify)" do
12
+ assert_equal "stones and gravel", Concrete.pour
13
+ assert_equal "glug glug", Jug.pour
14
+
15
+ Concrete.stubs!(:pour).returns("dust and plaster")
16
+
17
+ 3.times do
18
+ assert_equal "dust and plaster", Concrete.pour
19
+ end
20
+
21
+ assert_equal "glug glug", Jug.pour, "Jug's 'pour' method broken"
22
+ assert_equal "stones and gravel", Concrete._hardmock_original_pour, "Original 'pour' method not aliased"
23
+
24
+ assert_equal "For roads", Concrete.describe, "'describe' method broken"
25
+
26
+ verify_mocks
27
+
28
+ assert_equal "stones and gravel", Concrete.pour, "'pour' method not restored"
29
+ assert_equal "For roads", Concrete.describe, "'describe' method broken after verify"
30
+
31
+ end
32
+
33
+ it "stubs several class methods" do
34
+ Concrete.stubs!(:pour).returns("sludge")
35
+ Concrete.stubs!(:describe).returns("awful")
36
+ Jug.stubs!(:pour).returns("milk")
37
+
38
+ assert_equal "sludge", Concrete.pour
39
+ assert_equal "awful", Concrete.describe
40
+ assert_equal "milk", Jug.pour
41
+
42
+ verify_mocks
43
+
44
+ assert_equal "stones and gravel", Concrete.pour
45
+ assert_equal "For roads", Concrete.describe
46
+ assert_equal "glug glug", Jug.pour
47
+ end
48
+
49
+ it "stubs instance methods" do
50
+ slab = Concrete.new
51
+ assert_equal "bonk", slab.hit
52
+
53
+ slab.stubs!(:hit).returns("slap")
54
+ assert_equal "slap", slab.hit, "'hit' not stubbed"
55
+
56
+ verify_mocks
57
+
58
+ assert_equal "bonk", slab.hit, "'hit' not restored"
59
+ end
60
+
61
+ it "stubs instance methods without breaking class methods or other instances" do
62
+ slab = Concrete.new
63
+ scrape = Concrete.new
64
+ assert_equal "an instance", slab.describe
65
+ assert_equal "an instance", scrape.describe
66
+ assert_equal "For roads", Concrete.describe
67
+
68
+ slab.stubs!(:describe).returns("new instance describe")
69
+ assert_equal "new instance describe", slab.describe, "'describe' on instance not stubbed"
70
+ assert_equal "an instance", scrape.describe, "'describe' on 'scrape' instance broken"
71
+ assert_equal "For roads", Concrete.describe, "'describe' class method broken"
72
+
73
+ verify_mocks
74
+
75
+ assert_equal "an instance", slab.describe, "'describe' instance method not restored"
76
+ assert_equal "an instance", scrape.describe, "'describe' on 'scrape' instance broken after restore"
77
+ assert_equal "For roads", Concrete.describe, "'describe' class method broken after restore"
78
+ end
79
+
80
+ should "not allow stubbing of nonexistant class methods" do
81
+ assert_error(Hardmock::StubbingError, /cannot stub/i, /class method/i, /Concrete.funky/) do
82
+ Concrete.stubs!(:funky)
83
+ end
84
+ end
85
+
86
+ should "not allow stubbing of nonexistant instance methods" do
87
+ assert_error(Hardmock::StubbingError, /cannot stub/i, /method/i, /Concrete#my_inst_mth/) do
88
+ Concrete.new.stubs!(:my_inst_mth)
89
+ end
90
+ end
91
+
92
+ it "does nothing with a runtime block when simply stubbing" do
93
+ prepare_hardmock_control
94
+ slab = Concrete.new
95
+ slab.stubs!(:hit) do |nothing|
96
+ raise "BOOOMM!"
97
+ end
98
+ slab.hit
99
+ verify_mocks
100
+ end
101
+
102
+
103
+ #
104
+ # Per-method mocking on classes or instances
105
+ #
106
+
107
+ it "mocks specific methods on existing classes, and returns the class method to normal after verification" do
108
+ prepare_hardmock_control
109
+ assert_equal "stones and gravel", Concrete.pour, "Concrete.pour is already messed up"
110
+
111
+ Concrete.expects!(:pour).returns("ALIGATORS")
112
+ assert_equal "ALIGATORS", Concrete.pour
113
+
114
+ verify_mocks
115
+ assert_equal "stones and gravel", Concrete.pour, "Concrete.pour not restored"
116
+ end
117
+
118
+ it "flunks if expected class method is not invoked" do
119
+ prepare_hardmock_control
120
+ Concrete.expects!(:pour).returns("ALIGATORS")
121
+ assert_error(Hardmock::VerifyError, /Concrete.pour/, /unmet expectations/i) do
122
+ verify_mocks
123
+ end
124
+ clear_expectations
125
+ end
126
+
127
+ it "supports all normal mock functionality for class methods" do
128
+ prepare_hardmock_control
129
+ Concrete.expects!(:pour, "two tons").returns("mice")
130
+ Concrete.expects!(:pour, "three tons").returns("cats")
131
+ Concrete.expects!(:pour, "four tons").raises("Can't do it")
132
+ Concrete.expects!(:pour) do |some, args|
133
+ "==#{some}+#{args}=="
134
+ end
135
+
136
+ assert_equal "mice", Concrete.pour("two tons")
137
+ assert_equal "cats", Concrete.pour("three tons")
138
+ assert_error(RuntimeError, /Can't do it/) do
139
+ Concrete.pour("four tons")
140
+ end
141
+ assert_equal "==first+second==", Concrete.pour("first","second")
142
+ end
143
+
144
+
145
+ it "enforces inter-mock ordering when mocking class methods" do
146
+ create_mocks :truck, :foreman
147
+
148
+ @truck.expects.backup
149
+ Concrete.expects!(:pour, "something")
150
+ @foreman.expects.shout
151
+
152
+ @truck.backup
153
+ assert_error Hardmock::ExpectationError, /wrong/i, /expected call/i, /Concrete.pour/ do
154
+ @foreman.shout
155
+ end
156
+ assert_error Hardmock::VerifyError, /unmet expectations/i, /foreman.shout/ do
157
+ verify_mocks
158
+ end
159
+ clear_expectations
160
+ end
161
+
162
+ should "not allow mocking non-existant class methods" do
163
+ prepare_hardmock_control
164
+ assert_error Hardmock::StubbingError, /non-existant/, /something/ do
165
+ Concrete.expects!(:something)
166
+ end
167
+ end
168
+
169
+ it "mocks specific methods on existing instances, then restore them after verify" do
170
+ prepare_hardmock_control
171
+ slab = Concrete.new
172
+ assert_equal "bonk", slab.hit
173
+
174
+ slab.expects!(:hit).returns("slap")
175
+ assert_equal "slap", slab.hit, "'hit' not stubbed"
176
+
177
+ verify_mocks
178
+ assert_equal "bonk", slab.hit, "'hit' not restored"
179
+ end
180
+
181
+ it "flunks if expected instance method is not invoked" do
182
+ prepare_hardmock_control
183
+ slab = Concrete.new
184
+ slab.expects!(:hit)
185
+
186
+ assert_error Hardmock::VerifyError, /unmet expectations/i, /Concrete.hit/ do
187
+ verify_mocks
188
+ end
189
+ clear_expectations
190
+ end
191
+
192
+ it "supports all normal mock functionality for instance methods" do
193
+ prepare_hardmock_control
194
+ slab = Concrete.new
195
+
196
+ slab.expects!(:hit, "soft").returns("hey")
197
+ slab.expects!(:hit, "hard").returns("OOF")
198
+ slab.expects!(:hit).raises("stoppit")
199
+ slab.expects!(:hit) do |some, args|
200
+ "==#{some}+#{args}=="
201
+ end
202
+
203
+ assert_equal "hey", slab.hit("soft")
204
+ assert_equal "OOF", slab.hit("hard")
205
+ assert_error(RuntimeError, /stoppit/) do
206
+ slab.hit
207
+ end
208
+ assert_equal "==first+second==", slab.hit("first","second")
209
+
210
+ end
211
+
212
+ it "enforces inter-mock ordering when mocking instance methods" do
213
+ create_mocks :truck, :foreman
214
+ slab1 = Concrete.new
215
+ slab2 = Concrete.new
216
+
217
+ @truck.expects.backup
218
+ slab1.expects!(:hit)
219
+ @foreman.expects.shout
220
+ slab2.expects!(:hit)
221
+ @foreman.expects.whatever
222
+
223
+ @truck.backup
224
+ slab1.hit
225
+ @foreman.shout
226
+ assert_error Hardmock::ExpectationError, /wrong/i, /expected call/i, /Concrete.hit/ do
227
+ @foreman.whatever
228
+ end
229
+ assert_error Hardmock::VerifyError, /unmet expectations/i, /foreman.whatever/ do
230
+ verify_mocks
231
+ end
232
+ clear_expectations
233
+ end
234
+
235
+ should "not allow mocking non-existant instance methods" do
236
+ prepare_hardmock_control
237
+ slab = Concrete.new
238
+ assert_error Hardmock::StubbingError, /non-existant/, /something/ do
239
+ slab.expects!(:something)
240
+ end
241
+ end
242
+
243
+ should "support expectations that deal with runtime blocks"
244
+
245
+ #
246
+ # HELPERS
247
+ #
248
+
249
+ class Concrete
250
+ def self.pour
251
+ "stones and gravel"
252
+ end
253
+
254
+ def self.describe
255
+ "For roads"
256
+ end
257
+
258
+ def hit
259
+ "bonk"
260
+ end
261
+
262
+ def describe
263
+ "an instance"
264
+ end
265
+ end
266
+
267
+ class Jug
268
+ def self.pour
269
+ "glug glug"
270
+ end
271
+ end
272
+
273
+ end
274
+
data/test/test_helper.rb CHANGED
@@ -20,4 +20,27 @@ class Test::Unit::TestCase
20
20
  end
21
21
  return false
22
22
  end
23
+
24
+ def self.it(str, &block)
25
+ make_test_case "it", str, &block
26
+ end
27
+
28
+ def self.should(str, &block)
29
+ make_test_case "should", str, &block
30
+ end
31
+
32
+ def self.make_test_case(prefix, str, &block)
33
+ tname = self.name.sub(/Test$/,'')
34
+ if block
35
+ define_method "test #{prefix} #{str}" do
36
+ # phrase = str
37
+ # phrase = prefix + " " + phrase unless prefix == "it"
38
+ # puts "#{tname} #{phrase}..."
39
+ instance_eval &block
40
+ end
41
+ else
42
+ puts ">>> UNIMPLEMENTED CASE: #{tname}: #{str}"
43
+ end
44
+ end
45
+
23
46
  end
@@ -1,6 +1,7 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
2
  require 'hardmock/expectation'
3
3
  require 'hardmock/errors'
4
+ require 'assert_error'
4
5
 
5
6
  class ExpectationTest < Test::Unit::TestCase
6
7
  include Hardmock
@@ -344,4 +345,14 @@ class ExpectationTest < Test::Unit::TestCase
344
345
  assert_match(/"a", "b", "c"/i, err.message)
345
346
  assert_equal [], things, "Wrong things"
346
347
  end
348
+
349
+ def test_yields_bad_block_arity
350
+ se = Expectation.new(:mock => @mock, :method => 'do_later', :arguments => [] )
351
+ se.yields
352
+
353
+ assert_error Hardmock::ExpectationError, /block/i, /expected/i, /no param/i, /got 2/i do
354
+ se.apply_method_call(@mock,'do_later',[],lambda { |doesnt,match| raise "Surprise!" } )
355
+ end
356
+ end
357
+
347
358
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: hardmock
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.2.3
7
- date: 2007-04-28 00:00:00 -04:00
6
+ version: 1.3.0
7
+ date: 2007-11-12 00:00:00 -05:00
8
8
  summary: A strict, ordered, expectation-oriented mock object library.
9
9
  require_paths:
10
10
  - lib
@@ -38,6 +38,7 @@ files:
38
38
  - lib/hardmock/method_cleanout.rb
39
39
  - lib/hardmock/mock.rb
40
40
  - lib/hardmock/mock_control.rb
41
+ - lib/hardmock/stubbing.rb
41
42
  - lib/hardmock/trapper.rb
42
43
  - lib/hardmock/utils.rb
43
44
  - lib/tasks/rdoc_options.rb
@@ -46,6 +47,7 @@ files:
46
47
  - test/functional/auto_verify_test.rb
47
48
  - test/functional/direct_mock_usage_test.rb
48
49
  - test/functional/hardmock_test.rb
50
+ - test/functional/stubbing_test.rb
49
51
  - test/unit/expectation_builder_test.rb
50
52
  - test/unit/expectation_test.rb
51
53
  - test/unit/expector_test.rb
@@ -66,6 +68,7 @@ test_files:
66
68
  - test/functional/auto_verify_test.rb
67
69
  - test/functional/direct_mock_usage_test.rb
68
70
  - test/functional/hardmock_test.rb
71
+ - test/functional/stubbing_test.rb
69
72
  - test/unit/expectation_builder_test.rb
70
73
  - test/unit/expectation_test.rb
71
74
  - test/unit/expector_test.rb