flexmock 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,108 @@
1
+ = FlexMock 0.7.1 Released
2
+
3
+ FlexMock is a flexible mocking library for use in unit testing and
4
+ behavior specification in Ruby. It is a minor release that contains
5
+ some new funtionality.
6
+
7
+ == New Features n 0.8.0
8
+
9
+ * When should_ignore_missing is specified, the mock will return an
10
+ undefined object for any method calls that are ignored.
11
+
12
+ * A user can also specify an undefined object to be returned
13
+ explicitly by using FlexMock.undefined.
14
+
15
+ * Expectations may now be marked with "by_default". Default
16
+ expectations will be used unless a non-default expectation is given
17
+ for a matching method name.
18
+
19
+ * The :respond_to? method on mocks will now accept multiple arguments.
20
+ This eases the testing of models in certains situations.
21
+
22
+ * An experimental view mocker method is availble to mock out views in
23
+ Rails controller tests. See flexmock/rails/view_mocking.rb for more
24
+ details.
25
+
26
+ == What is FlexMock?
27
+
28
+ FlexMock is a flexible framework for creating mock object for testing. When
29
+ running unit tests, it is often desirable to use isolate the objects being
30
+ tested from the "real world" by having them interact with simplified test
31
+ objects. Sometimes these test objects simply return values when called, other
32
+ times they verify that certain methods were called with particular arguments
33
+ in a particular order.
34
+
35
+ FlexMock makes creating these test objects easy.
36
+
37
+ === Features
38
+
39
+ * Easy integration with both Test::Unit and RSpec. Mocks created with the
40
+ flexmock method are automatically verified at the end of the test or
41
+ example.
42
+
43
+ * A fluent interface that allows mock behavior to be specified very
44
+ easily.
45
+
46
+ * A "record mode" where an existing implementation can record its
47
+ interaction with a mock for later validation against a new
48
+ implementation.
49
+
50
+ * Easy mocking of individual methods in existing, non-mock objects.
51
+
52
+ * Easy mocking of chains of method calls.
53
+
54
+ * The ability to cause classes to instantiate test instances (instead of real
55
+ instances) for the duration of a test.
56
+
57
+ === Example
58
+
59
+ Suppose you had a Dog object that wagged a tail when it was happy.
60
+ Something like this:
61
+
62
+ class Dog
63
+ def initialize(a_tail)
64
+ @tail = a_tail
65
+ end
66
+ def happy
67
+ @tail.wag
68
+ end
69
+ end
70
+
71
+ To test the +Dog+ class without a real +Tail+ object (perhaps because
72
+ real +Tail+ objects activate servos in some robotic equipment), you
73
+ can do something like this:
74
+
75
+ require 'test/unit'
76
+ require 'flexmock/test_unit'
77
+
78
+ class TestDog < Test::Unit::TestCase
79
+ def test_dog_wags_tail_when_happy
80
+ tail = flexmock("tail")
81
+ tail.should_receive(:wag).once
82
+ dog = Dog.new(tail)
83
+ dog.happy
84
+ end
85
+ end
86
+
87
+ FlexMock will automatically verify that the mocked tail object received the
88
+ message +wag+ exactly one time. If it doesn't, the test will not pass.
89
+
90
+ See the FlexMock documentation at http://flexmock.rubyforge.org for details on
91
+ specifying arguments and return values on mocked methods, as well as a simple
92
+ technique for mocking tail objects when the Dog class creates the tail objects
93
+ directly.
94
+
95
+ == Availability
96
+
97
+ You can make sure you have the latest version with a quick RubyGems command:
98
+
99
+ gem install flexmock (you may need root/admin privileges)
100
+
101
+ Otherwise, you can get it from the more traditional places:
102
+
103
+ Download:: http://rubyforge.org/project/showfiles.php?group_id=170
104
+
105
+ You will find documentation at: http://flexmock.rubyforge.org.
106
+
107
+ -- Jim Weirich
108
+
data/lib/flexmock/base.rb CHANGED
@@ -20,4 +20,5 @@ require 'flexmock/validators'
20
20
  require 'flexmock/recorder'
21
21
  require 'flexmock/mock_container'
22
22
  require 'flexmock/partial_mock'
23
+ require 'flexmock/undefined'
23
24
  require 'flexmock/deprecated_methods'
data/lib/flexmock/core.rb CHANGED
@@ -9,6 +9,7 @@
9
9
  # above copyright notice is included.
10
10
  #+++
11
11
 
12
+ require 'flexmock/errors'
12
13
  require 'flexmock/composite'
13
14
  require 'flexmock/ordering'
14
15
 
@@ -45,13 +46,6 @@ require 'flexmock/ordering'
45
46
  class FlexMock
46
47
  include Ordering
47
48
 
48
- # Error raised when flexmock is used incorrectly.
49
- class UsageError < RuntimeError
50
- end
51
-
52
- class MockError < RuntimeError
53
- end
54
-
55
49
  attr_reader :flexmock_name
56
50
  attr_accessor :flexmock_container
57
51
 
@@ -94,38 +88,50 @@ class FlexMock
94
88
  end
95
89
  alias mock_ignore_missing should_ignore_missing
96
90
 
91
+ def by_default
92
+ @last_expectation.by_default
93
+ self
94
+ end
95
+
97
96
  # Handle missing methods by attempting to look up a handler.
98
97
  def method_missing(sym, *args, &block)
99
98
  flexmock_wrap do
100
99
  if handler = @expectations[sym]
101
100
  args << block if block_given?
102
101
  handler.call(*args)
102
+ elsif @ignore_missing
103
+ FlexMock.undefined
103
104
  else
104
- super(sym, *args, &block) unless @ignore_missing
105
+ super(sym, *args, &block)
105
106
  end
106
107
  end
107
108
  end
108
109
 
109
110
  # Save the original definition of respond_to? for use a bit later.
110
- alias mock_respond_to? respond_to?
111
+ alias flexmock_respond_to? respond_to?
111
112
 
112
113
  # Override the built-in respond_to? to include the mocked methods.
113
- def respond_to?(sym)
114
+ def respond_to?(sym, *args)
114
115
  super || (@expectations[sym] ? true : @ignore_missing)
115
116
  end
116
117
 
117
118
  # Find the mock expectation for method sym and arguments.
118
- def flexmock_find_expectation(sym, *args)
119
- exp = @expectations[sym]
119
+ def flexmock_find_expectation(method_name, *args) # :nodoc:
120
+ exp = @expectations[method_name]
120
121
  exp ? exp.find_expectation(*args) : nil
121
122
  end
122
123
 
124
+ # Return the expectation director for a method name.
125
+ def flexmock_expectations_for(method_name) # :nodoc:
126
+ @expectations[method_name]
127
+ end
128
+
123
129
  # Override the built-in +method+ to include the mocked methods.
124
130
  def method(sym)
125
131
  @expectations[sym] || super
126
132
  rescue NameError => ex
127
133
  if @ignore_missing
128
- proc { }
134
+ proc { FlexMock.undefined }
129
135
  else
130
136
  raise ex
131
137
  end
@@ -151,13 +157,14 @@ class FlexMock
151
157
  # See Expectation for a list of declarators that can be used.
152
158
  #
153
159
  def should_receive(*args)
154
- ContainerHelper.parse_should_args(self, args) do |sym|
160
+ @last_expectation = ContainerHelper.parse_should_args(self, args) do |sym|
155
161
  @expectations[sym] ||= ExpectationDirector.new(sym)
156
162
  result = Expectation.new(self, sym)
157
163
  @expectations[sym] << result
158
- override_existing_method(sym) if mock_respond_to?(sym)
164
+ override_existing_method(sym) if flexmock_respond_to?(sym)
159
165
  result
160
166
  end
167
+ @last_expectation
161
168
  end
162
169
 
163
170
  # Declare that the mock object should expect methods by providing a
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 by Jim Weirich (jim@weirichhouse.org).
5
+ # All rights reserved.
6
+ #
7
+ # Permission is granted for use, copying, modification, distribution,
8
+ # and distribution of modified versions of this work as long as the
9
+ # above copyright notice is included.
10
+ #+++
11
+
12
+ require 'flexmock/noop'
13
+
14
+ class FlexMock
15
+
16
+ # Error raised when flexmock is used incorrectly.
17
+ class UsageError < ::RuntimeError
18
+ end
19
+
20
+ class MockError < ::RuntimeError
21
+ end
22
+
23
+ end
@@ -100,6 +100,11 @@ class FlexMock
100
100
  @count_validators.all? { |v| v.eligible?(@actual_count) }
101
101
  end
102
102
 
103
+ # Is this expectation constrained by any call counts?
104
+ def call_count_constrained?
105
+ ! @count_validators.empty?
106
+ end
107
+
103
108
  # Validate that the order
104
109
  def validate_order
105
110
  if @order_number
@@ -192,6 +197,34 @@ class FlexMock
192
197
  end
193
198
  alias :returns :and_return # :nodoc:
194
199
 
200
+ # Declare that the method returns and undefined object
201
+ # (FlexMock.undefined). Since the undefined object will always
202
+ # return itself for any message sent to it, it is a good "I don't
203
+ # care" value to return for methods that are commonly used in
204
+ # method chains.
205
+ #
206
+ # For example, if m.foo returns the undefined object, then:
207
+ #
208
+ # m.foo.bar.baz
209
+ #
210
+ # returns the undefined object without throwing an exception.
211
+ #
212
+ def and_return_undefined
213
+ and_return(FlexMock.undefined)
214
+ end
215
+ alias :returns_undefined :and_return_undefined
216
+
217
+ # :call-seq:
218
+ # and_yield(value1, value2, ...)
219
+ #
220
+ # Declare that the mocked method is expected to be given a block
221
+ # and that the block will be called with the values supplied to
222
+ # yield. If the mock is called multiple times, mulitple
223
+ # <tt>and_yield</tt> declarations can be used to supply different
224
+ # values on each call.
225
+ #
226
+ # An error is raised if the mocked method is not called with a
227
+ # block.
195
228
  def and_yield(*yield_values)
196
229
  @yield_queue << yield_values
197
230
  end
@@ -352,6 +385,11 @@ class FlexMock
352
385
  end
353
386
  private :define_ordered
354
387
 
388
+ def by_default
389
+ expectations = mock.flexmock_expectations_for(@sym)
390
+ expectations.defaultify_expectation(self) if expectations
391
+ end
392
+
355
393
  end
356
394
 
357
395
  ##########################################################################
@@ -10,9 +10,9 @@
10
10
  #+++
11
11
 
12
12
  require 'flexmock/noop'
13
+ require 'flexmock/errors'
13
14
 
14
15
  class FlexMock
15
-
16
16
  ####################################################################
17
17
  # The expectation director is responsible for routing calls to the
18
18
  # correct expectations for a given argument list.
@@ -23,6 +23,7 @@ class FlexMock
23
23
  def initialize(sym)
24
24
  @sym = sym
25
25
  @expectations = []
26
+ @defaults = []
26
27
  @expected_order = nil
27
28
  end
28
29
 
@@ -41,24 +42,43 @@ class FlexMock
41
42
  exp.verify_call(*args)
42
43
  end
43
44
 
44
- # Find an expectation matching the given arguments.
45
- def find_expectation(*args)
46
- @expectations.find { |e| e.match_args(args) && e.eligible? } ||
47
- @expectations.find { |e| e.match_args(args) }
48
- end
49
-
50
45
  # Append an expectation to this director.
51
46
  def <<(expectation)
52
47
  @expectations << expectation
53
48
  end
54
49
 
50
+ # Find an expectation matching the given arguments.
51
+ def find_expectation(*args) # :nodoc:
52
+ find_expectation_in(@expectations, *args) ||
53
+ find_expectation_in(@defaults, *args)
54
+ end
55
+
55
56
  # Do the post test verification for this directory. Check all the
56
- # expectations.
57
- def flexmock_verify
58
- @expectations.each do |exp|
57
+ # expectations. Only check the default expecatations if there are
58
+ # no non-default expectations.
59
+ def flexmock_verify # :nodoc:
60
+ (@expectations.empty? ? @defaults : @expectations).each do |exp|
59
61
  exp.flexmock_verify
60
62
  end
61
63
  end
64
+
65
+ # Move the last defined expectation a default.
66
+ def defaultify_expectation(exp) # :nodoc:
67
+ last_exp = @expectations.last
68
+ if last_exp != exp
69
+ fail UsageError,
70
+ "Cannot make a previously defined expection into a default"
71
+ end
72
+ @expectations.pop
73
+ @defaults << exp
74
+ end
75
+
76
+ private
77
+
78
+ def find_expectation_in(expectations, *args)
79
+ expectations.find { |e| e.match_args(args) && e.eligible? } ||
80
+ expectations.find { |e| e.match_args(args) }
81
+ end
62
82
  end
63
83
 
64
84
  end
@@ -175,7 +175,7 @@ class FlexMock
175
175
 
176
176
  # Return the next id for mocked models.
177
177
  def next_id
178
- @id_counter ||= 0
178
+ @id_counter ||= 10000
179
179
  @id_counter += 1
180
180
  end
181
181
 
@@ -44,7 +44,9 @@ class FlexMock
44
44
  @methods_proxied = []
45
45
  unless safe_mode
46
46
  MOCK_METHODS.each do |sym|
47
- add_mock_method(@obj, sym)
47
+ unless @obj.respond_to?(sym)
48
+ add_mock_method(@obj, sym)
49
+ end
48
50
  end
49
51
  end
50
52
  end
@@ -163,6 +165,11 @@ class FlexMock
163
165
  def flexmock_container=(container)
164
166
  end
165
167
 
168
+ # Forward the request for the expectation director to the mock.
169
+ def flexmock_expectations_for(method_name)
170
+ @mock.flexmock_expectations_for(method_name)
171
+ end
172
+
166
173
  private
167
174
 
168
175
  # The singleton class of the object.
@@ -0,0 +1,30 @@
1
+ require 'flexmock'
2
+
3
+ class FlexMock
4
+ module MockContainer
5
+
6
+ # Declare that the Rails controller under test should render the
7
+ # named view. If a view template name is given, it will be an
8
+ # error if the named view is not rendered during the execution of
9
+ # the contoller action. If no template name is given, then the
10
+ # any view may be rendered. If no view is actually rendered, then
11
+ # a assertion failure will occur.
12
+ def should_render_view(template_name=nil)
13
+ view = flexmock("MockView")
14
+ view.should_receive(
15
+ :assigns => {},
16
+ :render_file => true,
17
+ :first_render => "dummy_template"
18
+ )
19
+ if template_name
20
+ view.should_receive(:file_exists?).with(/#{template_name}$/).once.
21
+ and_return(true)
22
+ end
23
+ view.should_receive(:file_exists?).with(any).and_return(true)
24
+ view_class = flexmock("MockViewClasss")
25
+ view_class.should_receive(:new).and_return(view)
26
+ flexmock(@controller.class).should_receive(:view_class).once.
27
+ and_return(view_class)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 by Jim Weirich (jim@weirichhouse.org).
5
+ # All rights reserved.
6
+ #
7
+ # Permission is granted for use, copying, modification, distribution,
8
+ # and distribution of modified versions of this work as long as the
9
+ # above copyright notice is included.
10
+ #+++
11
+
12
+ # Require the modules needed for rails oriented mocking.
13
+
14
+ require 'flexmock'
15
+ require 'flexmock/rails/view_mocking'
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003, 2004, 2005, 2006, 2007 by Jim Weirich (jim@weirichhouse.org).
5
+ # All rights reserved.
6
+
7
+ # Permission is granted for use, copying, modification, distribution,
8
+ # and distribution of modified versions of this work as long as the
9
+ # above copyright notice is included.
10
+ #+++
11
+
12
+ class FlexMock
13
+
14
+ # Undefined is a self preserving undefined object. The result of
15
+ # any interaction with the undefined object will be the undefined
16
+ # object itself.
17
+ class Undefined
18
+ def method_missing(sym, *args, &block)
19
+ self
20
+ end
21
+
22
+ def to_s
23
+ "-UNDEFINED-"
24
+ end
25
+
26
+ def inspect
27
+ to_s
28
+ end
29
+
30
+ def clone
31
+ self
32
+ end
33
+
34
+ def coerce(other)
35
+ [FlexMock.undefined, FlexMock.undefined]
36
+ end
37
+ end
38
+
39
+ # Single instance of undefined
40
+ @undefined = Undefined.new
41
+
42
+ # Undefined is normally available as FlexMock.undefined
43
+ def self.undefined
44
+ @undefined
45
+ end
46
+
47
+ class << Undefined
48
+ private :new
49
+ end
50
+ end
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'flexmock'
5
+
6
+ class FlexMock
7
+ module StubsAndExpects
8
+ def expects(*args)
9
+ result = should_receive(*args)
10
+ result.at_least.once unless result.call_count_constrained?
11
+ result
12
+ end
13
+ def stubs(*args)
14
+ should_receive(*args)
15
+ end
16
+ end
17
+
18
+ module MockContainer
19
+ alias :mock :flexmock
20
+ alias :stub :flexmock
21
+ end
22
+
23
+ include StubsAndExpects
24
+
25
+ class PartialMockProxy
26
+ include StubsAndExpects
27
+ MOCK_METHODS << :stubs << :expects
28
+ end
29
+ end
30
+
31
+ class AliasingTest < Test::Unit::TestCase
32
+ include FlexMock::TestCase
33
+
34
+ def test_mocking
35
+ m = mock("a cute dog").expects(:pat).twice.and_return(:woof!).mock
36
+ assert_equal :woof!, m.pat
37
+ assert_equal :woof!, m.pat
38
+ end
39
+
40
+ def test_once_mocking
41
+ m = mock("a cute dog").expects(:pat).and_return(:woof!).mock
42
+ end
43
+
44
+ def test_twice_mocking
45
+ m = mock("a cute dog").expects(:pat).and_return(:woof!).twice.mock
46
+ assert_raises(Test::Unit::AssertionFailedError) { m.flexmock_verify }
47
+ end
48
+
49
+ def test_stubbing
50
+ m = stub("a cute dog").expects(:pat).and_return(:woof!).mock
51
+ assert_equal :woof!, m.pat
52
+ end
53
+
54
+ def test_partial
55
+ obj = Object.new
56
+ stub(obj).stubs(:wag).and_return(:tail)
57
+ assert_equal :tail, obj.wag
58
+ end
59
+ end
60
+
@@ -184,7 +184,7 @@ class TestFlexMock < Test::Unit::TestCase
184
184
  @mock.mock_ignore_missing
185
185
  method_proc = @mock.method(:plugh)
186
186
  assert_not_nil method_proc
187
- method_proc.call
187
+ assert_equal FlexMock.undefined, method_proc.call
188
188
  end
189
189
  end
190
190