flexmock 0.7.1 → 0.8.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.
@@ -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