test-unit-mock 0.30
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.
- data/COPYING +58 -0
- data/ChangeLog +262 -0
- data/README +203 -0
- data/install.rb +85 -0
- data/misc/readmecode.rb +125 -0
- data/mock.rb +467 -0
- data/test-unit-mock.gemspec +20 -0
- data/test.rb +327 -0
- data/utils.rb +345 -0
- metadata +59 -0
data/install.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# $Date: 2003/03/04 23:33:31 $
|
4
|
+
# Copyright (c) 2000 Masatoshi SEKI
|
5
|
+
#
|
6
|
+
# install.rb is copyrighted free software by Masatoshi SEKI.
|
7
|
+
# You can redistribute it and/or modify it under the same term as Ruby.
|
8
|
+
|
9
|
+
require 'rbconfig'
|
10
|
+
require 'find'
|
11
|
+
require 'ftools'
|
12
|
+
|
13
|
+
include Config
|
14
|
+
|
15
|
+
class Installer
|
16
|
+
protected
|
17
|
+
def install(from, to, mode = nil, verbose = false)
|
18
|
+
str = "install '#{from}' to '#{to}'"
|
19
|
+
str += ", mode=#{mode}" if mode
|
20
|
+
puts str if verbose
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def makedirs(*dirs)
|
25
|
+
for d in dirs
|
26
|
+
puts "mkdir #{d}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(test=false)
|
31
|
+
@version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
|
32
|
+
@libdir = File.join(CONFIG["libdir"], "ruby", @version)
|
33
|
+
@sitelib = find_site_libdir
|
34
|
+
@ftools = (test) ? self : File
|
35
|
+
end
|
36
|
+
public
|
37
|
+
attr_reader(:libdir, :sitelib)
|
38
|
+
|
39
|
+
private
|
40
|
+
def find_site_libdir
|
41
|
+
site_libdir = $:.find {|x| x =~ /site_ruby$/}
|
42
|
+
if !site_libdir
|
43
|
+
site_libdir = File.join(@libdir, "site_ruby")
|
44
|
+
elsif site_libdir !~ Regexp.quote(@version)
|
45
|
+
site_libdir = File.join(site_libdir, @version)
|
46
|
+
end
|
47
|
+
site_libdir
|
48
|
+
end
|
49
|
+
|
50
|
+
public
|
51
|
+
def files_in_dir(dir)
|
52
|
+
list = []
|
53
|
+
Find.find(dir) do |f|
|
54
|
+
list.push(f)
|
55
|
+
end
|
56
|
+
list
|
57
|
+
end
|
58
|
+
|
59
|
+
public
|
60
|
+
def install_files(srcdir, files, destdir=@sitelib)
|
61
|
+
path = []
|
62
|
+
dir = []
|
63
|
+
|
64
|
+
for f in files
|
65
|
+
next if (f = f[srcdir.length+1..-1]) == nil
|
66
|
+
path.push f if File.ftype(File.join(srcdir, f)) == 'file'
|
67
|
+
dir |= [ File.dirname(File.join(destdir, f)) ]
|
68
|
+
end
|
69
|
+
@ftools.makedirs(*dir)
|
70
|
+
for f in path
|
71
|
+
@ftools.install(File.join(srcdir, f), File.join(destdir, f), nil, true)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
public
|
76
|
+
def install_rb
|
77
|
+
intall_files('lib', files_in_dir('lib'))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if __FILE__ == $0
|
82
|
+
inst = Installer.new(ARGV.shift == '-n')
|
83
|
+
inst.install_files( '.', ['./mock.rb'], File::join(inst.sitelib, 'test/unit') )
|
84
|
+
end
|
85
|
+
|
data/misc/readmecode.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift ".", ".."
|
4
|
+
|
5
|
+
require File::join( File::dirname(File::dirname( __FILE__ )), 'utils.rb' )
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'mock'
|
9
|
+
rescue LoadError
|
10
|
+
require 'test/unit/mock'
|
11
|
+
end
|
12
|
+
require 'socket'
|
13
|
+
require 'test/unit'
|
14
|
+
|
15
|
+
# This is just a test to make sure the code in the README works as advertised.
|
16
|
+
|
17
|
+
class MockTestExperiment < Test::Unit::TestCase
|
18
|
+
|
19
|
+
def setup
|
20
|
+
# Create the mock object
|
21
|
+
@mockSocket = Test::Unit::MockObject( TCPSocket ).new
|
22
|
+
|
23
|
+
# Make the #addr method return three cycling values (which will be repeated
|
24
|
+
# when it reaches the end
|
25
|
+
@mockSocket.setReturnValues( :addr => [
|
26
|
+
["AF_INET", 23, "localhost", "127.0.0.1"],
|
27
|
+
["AF_INET", 80, "slashdot.org", "66.35.250.150"],
|
28
|
+
["AF_INET", 2401, "helium.ruby-lang.org", "210.251.121.214"],
|
29
|
+
] )
|
30
|
+
|
31
|
+
# Make the #write and #read methods call a Proc and a Method, respectively
|
32
|
+
@mockSocket.setReturnValues( :write => Proc::new {|str| str.length},
|
33
|
+
:read => method(:fakeRead) )
|
34
|
+
|
35
|
+
# Set up the #getsockopt method to return a value based on the arguments
|
36
|
+
# given:
|
37
|
+
@mockSocket.setReturnValues( :getsockopt => {
|
38
|
+
[Socket::SOL_TCP, Socket::TCP_NODELAY] => [0].pack("i_"),
|
39
|
+
[Socket::SOL_SOCKET, Socket::SO_REUSEADDR] => [1].pack("i_"),
|
40
|
+
} )
|
41
|
+
|
42
|
+
@mockSocket.setCallOrder( :addr, :getsockopt, :write, :read, :write, :read )
|
43
|
+
@mockSocket.strictCallOrder = true
|
44
|
+
end
|
45
|
+
alias :set_up :setup
|
46
|
+
|
47
|
+
def teardown
|
48
|
+
@mockSocket = nil
|
49
|
+
end
|
50
|
+
alias :tear_down :teardown
|
51
|
+
|
52
|
+
|
53
|
+
# A method to fake reading from the socket.
|
54
|
+
def fakeRead( len )
|
55
|
+
return "x" * len
|
56
|
+
end
|
57
|
+
|
58
|
+
# This should pass
|
59
|
+
def test_correctorder
|
60
|
+
@mockSocket.activate
|
61
|
+
|
62
|
+
# Call the methods in the correct order
|
63
|
+
assert_nothing_raised {
|
64
|
+
@mockSocket.addr
|
65
|
+
@mockSocket.getsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY )
|
66
|
+
@mockSocket.write( "foo" )
|
67
|
+
@mockSocket.read( 1024 )
|
68
|
+
@mockSocket.write( "bar" )
|
69
|
+
@mockSocket.read( 4096 )
|
70
|
+
}
|
71
|
+
|
72
|
+
if $DEBUG
|
73
|
+
puts "Call trace:\n " + @mockSocket.callTrace.join("\n ")
|
74
|
+
end
|
75
|
+
|
76
|
+
# Check method call order on the mocked socket
|
77
|
+
@mockSocket.verify
|
78
|
+
end
|
79
|
+
|
80
|
+
# This should fail with an incorrect order message
|
81
|
+
def test_incorrectorder
|
82
|
+
@mockSocket.activate
|
83
|
+
|
84
|
+
# Call the methods in the correct order
|
85
|
+
assert_nothing_raised {
|
86
|
+
@mockSocket.addr
|
87
|
+
@mockSocket.getsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY )
|
88
|
+
@mockSocket.read( 1024 )
|
89
|
+
@mockSocket.write( "foo" )
|
90
|
+
@mockSocket.write( "bar" )
|
91
|
+
@mockSocket.read( 4096 )
|
92
|
+
}
|
93
|
+
|
94
|
+
if $DEBUG
|
95
|
+
puts "Call trace:\n " + @mockSocket.callTrace.join("\n ")
|
96
|
+
end
|
97
|
+
|
98
|
+
# Check method call order on the mocked socket
|
99
|
+
@mockSocket.verify
|
100
|
+
end
|
101
|
+
|
102
|
+
# This should fail with a 'missing' message
|
103
|
+
def test_missingcall
|
104
|
+
@mockSocket.activate
|
105
|
+
|
106
|
+
# Call the methods in the correct order
|
107
|
+
assert_nothing_raised {
|
108
|
+
@mockSocket.addr
|
109
|
+
@mockSocket.getsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY )
|
110
|
+
@mockSocket.write( "foo" )
|
111
|
+
@mockSocket.read( 1024 )
|
112
|
+
@mockSocket.write( "bar" )
|
113
|
+
}
|
114
|
+
|
115
|
+
if $DEBUG
|
116
|
+
puts "Call trace:\n " + @mockSocket.callTrace.join("\n ")
|
117
|
+
end
|
118
|
+
|
119
|
+
# Check method call order on the mocked socket
|
120
|
+
@mockSocket.verify
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
|
data/mock.rb
ADDED
@@ -0,0 +1,467 @@
|
|
1
|
+
# Ruby/Mock version 1.0
|
2
|
+
#
|
3
|
+
# A class for conveniently building mock objects in Test::Unit test cases. It is
|
4
|
+
# based on ideas in Ruby/Mock by Nat Pryce <nat.pryce@b13media.com>, which is a
|
5
|
+
# class for doing much the same thing for RUnit test cases.
|
6
|
+
#
|
7
|
+
# == Examples
|
8
|
+
#
|
9
|
+
# For the examples below, it is assumed that a hypothetical '<tt>Adapter</tt>'
|
10
|
+
# class is needed to test whatever class is being tested. It has two instance
|
11
|
+
# methods in addition to its initializer: <tt>read</tt>, which takes no
|
12
|
+
# arguments and returns data read from the adapted source, and <tt>write</tt>,
|
13
|
+
# which takes a String and writes as much as it can to the adapted destination,
|
14
|
+
# returning any that is left over.
|
15
|
+
#
|
16
|
+
# # With the in-place mock-object constructor, you can make an instance of a
|
17
|
+
# # one-off anonymous test class:
|
18
|
+
# mockAdapter = Test::Unit::MockObject( Adapter ).new
|
19
|
+
#
|
20
|
+
# # Now set up some return values for the next test:
|
21
|
+
# mockAdapter.setReturnValues( :read => "",
|
22
|
+
# :write => Proc::new {|data| data[-20,20]} )
|
23
|
+
#
|
24
|
+
# # Mandate a certain order to the calls
|
25
|
+
# mockAdapter.setCallOrder( :read, :read, :read, :write, :read )
|
26
|
+
#
|
27
|
+
# # Now start the mock object recording interactions with it
|
28
|
+
# mockAdapter.activate
|
29
|
+
#
|
30
|
+
# # Send the adapter to the tested object and run the tests
|
31
|
+
# testedObject.setAdapter( mockAdapter )
|
32
|
+
# ...
|
33
|
+
#
|
34
|
+
# # Now check the order of method calls on the mock object against the expected
|
35
|
+
# # order.
|
36
|
+
# mockAdapter.verify
|
37
|
+
#
|
38
|
+
# If you require more advanced functionality in your mock class, you can also
|
39
|
+
# use the anonymous class returned by the Test::Unit::MockObject factory method
|
40
|
+
# as the superclass for your own mockup like this:
|
41
|
+
#
|
42
|
+
# # Create a mocked adapter class
|
43
|
+
# class MockAdapter < Test::Unit::MockObject( Adapter )
|
44
|
+
#
|
45
|
+
# def initialize
|
46
|
+
# super
|
47
|
+
# setCallOrder( :read, :read, :read, :write, :read )
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# def read( *args )
|
51
|
+
# @readargs = args
|
52
|
+
# super # Call the mocked method to record the call
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# def write( *args )
|
56
|
+
# @writeargs = args
|
57
|
+
# super # Call the mocked method to record the call
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# == Note
|
62
|
+
#
|
63
|
+
# All the testing and setup methods in the Test::Unit::Mockup class (the
|
64
|
+
# abstract class that new MockObjects inherit from) have two aliases for your
|
65
|
+
# convenience:
|
66
|
+
#
|
67
|
+
# * Each one has a double-underscore alias so that methods which collide with
|
68
|
+
# them from the mocked class don't obscure them. Eg.,
|
69
|
+
#
|
70
|
+
# mockAdapter.__setCallOrder( :read, :read, :read, :write, :read )
|
71
|
+
#
|
72
|
+
# can be used instead of the call from the example above if the Adapter for
|
73
|
+
# some reason already has a 'setCallOrder' instance method.
|
74
|
+
#
|
75
|
+
# * Non-camelCase versions of the methods are also provided. Eg.,
|
76
|
+
#
|
77
|
+
# mockAdapter.set_call_order( :read, :read, :read, :write, :read )
|
78
|
+
#
|
79
|
+
# will work, too. Double-underscored *and* non camelCased aliases are not
|
80
|
+
# defined; if anyone complains, I'll add them.
|
81
|
+
#
|
82
|
+
# == Rcsid
|
83
|
+
#
|
84
|
+
# $Id: mock.rb,v 1.10 2003/10/01 15:11:40 deveiant Exp $
|
85
|
+
#
|
86
|
+
# == Authors
|
87
|
+
#
|
88
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
89
|
+
#
|
90
|
+
#
|
91
|
+
#
|
92
|
+
|
93
|
+
require 'algorithm/diff'
|
94
|
+
|
95
|
+
require 'test/unit'
|
96
|
+
require 'test/unit/assertions'
|
97
|
+
|
98
|
+
module Test
|
99
|
+
module Unit
|
100
|
+
|
101
|
+
# The Regexp that matches methods which will not be mocked.
|
102
|
+
UnmockedMethods = %r{^(
|
103
|
+
__ # __id__, __call__, etc.
|
104
|
+
|inspect # Useful as-is for debugging, and hard to fake
|
105
|
+
|kind_of\?|is_a\?|instance_of\? # / Can't fake simply -- delegated to the
|
106
|
+
|type|class # \ actual underlying class
|
107
|
+
|method|send|respond_to\? # These will work fine as-is
|
108
|
+
|hash # There's no good way to fake this
|
109
|
+
)}x
|
110
|
+
|
111
|
+
### An abstract base class that provides the setup and testing methods
|
112
|
+
### to concrete mock objects.
|
113
|
+
class Mockup
|
114
|
+
|
115
|
+
include Test::Unit::Assertions
|
116
|
+
|
117
|
+
### Instantiate and return a new mock object after recording the
|
118
|
+
### specified args.
|
119
|
+
def initialize( *args )
|
120
|
+
@args = args
|
121
|
+
@calls = []
|
122
|
+
@activated = nil
|
123
|
+
@returnValues = Hash::new( true )
|
124
|
+
@callOrder = []
|
125
|
+
@strictCallOrder = false
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
#########################################################
|
130
|
+
### F A K E T Y P E - C H E C K I N G M E T H O D S
|
131
|
+
#########################################################
|
132
|
+
|
133
|
+
# Handle #type and #class methods
|
134
|
+
alias :__class :class
|
135
|
+
def class # :nodoc:
|
136
|
+
return __class.mockedClass
|
137
|
+
end
|
138
|
+
undef_method :type
|
139
|
+
alias_method :type, :class
|
140
|
+
|
141
|
+
# Fake instance_of?, kind_of?, and is_a? with the mocked class
|
142
|
+
def instance_of?( klass ) # :nodoc:
|
143
|
+
self.class == klass
|
144
|
+
end
|
145
|
+
def kind_of?( klass ) # :nodoc:
|
146
|
+
self.class <= klass
|
147
|
+
end
|
148
|
+
alias_method :is_a?, :kind_of?
|
149
|
+
|
150
|
+
|
151
|
+
#########################################################
|
152
|
+
### S E T U P M E T H O D S
|
153
|
+
#########################################################
|
154
|
+
|
155
|
+
### Set the return value for one or more methods. The <tt>hash</tt>
|
156
|
+
### should contain one or more key/value pairs, the key of which is
|
157
|
+
### the symbol which corresponds to the method being configured, and
|
158
|
+
### the value which is a specification of the value to be
|
159
|
+
### returned. More complex returns can be configured with one the
|
160
|
+
### following types of values:
|
161
|
+
###
|
162
|
+
### [<tt>Method</tt> or <tt>Proc</tt>]
|
163
|
+
### A <tt>Method</tt> or <tt>Proc</tt> object will be called with
|
164
|
+
### the arguments given to the method, and whatever it returns
|
165
|
+
### will be used as the return value.
|
166
|
+
### [<tt>Array</tt>]
|
167
|
+
### The first value in the <tt>Array</tt> will be rotated to the
|
168
|
+
### end of the Array and returned.
|
169
|
+
### [<tt>Hash</tt>]
|
170
|
+
### The Array of method arguments will be used as a key, and
|
171
|
+
### whatever the corresponding value in the given Hash is will be
|
172
|
+
### returned.
|
173
|
+
###
|
174
|
+
### Any other value will be returned as-is. To return one of the
|
175
|
+
### above types of objects literally, just wrap it in an Array like
|
176
|
+
### so:
|
177
|
+
###
|
178
|
+
### # Return a literal Proc without calling it:
|
179
|
+
### mockObj.setReturnValues( :meth => [myProc] )
|
180
|
+
###
|
181
|
+
### # Return a literal Array:
|
182
|
+
### mockObj.setReturnValues( :meth => [["an", "array", "of", "stuff"]] )
|
183
|
+
def setReturnValues( hash )
|
184
|
+
@returnValues.update hash
|
185
|
+
end
|
186
|
+
alias_method :__setReturnValues, :setReturnValues
|
187
|
+
alias_method :set_return_values, :setReturnValues
|
188
|
+
|
189
|
+
|
190
|
+
### Set up an expected method call order and argument specification
|
191
|
+
### to be checked when #verify is called to the methods specified by
|
192
|
+
### the given <tt>symbols</tt>.
|
193
|
+
def setCallOrder( *symbols )
|
194
|
+
@callOrder = symbols
|
195
|
+
end
|
196
|
+
alias_method :__setCallOrder, :setCallOrder
|
197
|
+
alias_method :set_call_order, :setCallOrder
|
198
|
+
|
199
|
+
|
200
|
+
### Set the strict call order flag. When #verify is called, the
|
201
|
+
### methods specified in calls to #setCallOrder will be checked
|
202
|
+
### against the actual methods that were called on the object. If
|
203
|
+
### this flag is set to <tt>true</tt>, any deviation (missing,
|
204
|
+
### misordered, or extra calls) results in a failed assertion. If it
|
205
|
+
### is not set, other method calls may be interspersed between the
|
206
|
+
### calls specified without effect, but a missing or misordered
|
207
|
+
### method still fails.
|
208
|
+
def strictCallOrder=( flag )
|
209
|
+
@strictCallOrder = true if flag
|
210
|
+
end
|
211
|
+
alias_method :__strictCallOrder=, :strictCallOrder=
|
212
|
+
alias_method :strict_call_order=, :strictCallOrder=
|
213
|
+
|
214
|
+
|
215
|
+
### Returns true if strict call order checking is enabled.
|
216
|
+
def strictCallOrder?
|
217
|
+
@strictCallOrder
|
218
|
+
end
|
219
|
+
alias_method :__strictCallOrder?, :strictCallOrder?
|
220
|
+
alias_method :strict_call_order?, :strictCallOrder?
|
221
|
+
|
222
|
+
|
223
|
+
#########################################################
|
224
|
+
### T E S T I N G M E T H O D S
|
225
|
+
#########################################################
|
226
|
+
|
227
|
+
### Returns an array of Strings describing, in cronological order,
|
228
|
+
### what method calls were registered with the object.
|
229
|
+
def callTrace
|
230
|
+
return [] unless @activated
|
231
|
+
@calls.collect {|call|
|
232
|
+
"%s( %s ) at %0.5f seconds from %s" % [
|
233
|
+
call[:method].to_s,
|
234
|
+
call[:args].collect {|arg| arg.inspect}.join(","),
|
235
|
+
call[:time] - @activated,
|
236
|
+
call[:caller][0]
|
237
|
+
]
|
238
|
+
}
|
239
|
+
end
|
240
|
+
alias_method :__callTrace, :callTrace
|
241
|
+
alias_method :call_trace, :callTrace
|
242
|
+
|
243
|
+
|
244
|
+
### Returns an array of Strings describing, in cronological order,
|
245
|
+
### what method calls were registered with the object along with a
|
246
|
+
### full stacktrace for each call.
|
247
|
+
def fullCallTrace
|
248
|
+
return [] unless @activated
|
249
|
+
@calls.collect {|call|
|
250
|
+
"%s( %s ) at %0.5f seconds. Called from %s\n\t%s" % [
|
251
|
+
call[:method].to_s,
|
252
|
+
call[:args].collect {|arg| arg.inspect}.join(","),
|
253
|
+
call[:time] - @activated,
|
254
|
+
call[:caller][0],
|
255
|
+
call[:caller][1..-1].join("\n\t"),
|
256
|
+
]
|
257
|
+
}
|
258
|
+
end
|
259
|
+
alias_method :__fullCallTrace, :fullCallTrace
|
260
|
+
alias_method :full_call_trace, :fullCallTrace
|
261
|
+
|
262
|
+
|
263
|
+
### Turn on call registration -- begin testing.
|
264
|
+
def activate
|
265
|
+
raise "Already activated!" if @activated
|
266
|
+
self.__clear
|
267
|
+
@activated = Time::now
|
268
|
+
end
|
269
|
+
alias_method :__activate, :activate
|
270
|
+
|
271
|
+
|
272
|
+
### Verify the registered required methods were called with the
|
273
|
+
### specified args
|
274
|
+
def verify
|
275
|
+
raise "Cannot verify a mock object that has never been "\
|
276
|
+
"activated." unless @activated
|
277
|
+
return true if @callOrder.empty?
|
278
|
+
|
279
|
+
actualCallOrder = @calls.collect {|call| call[:method]}
|
280
|
+
diff = Diff::diff( @callOrder, actualCallOrder )
|
281
|
+
|
282
|
+
# In strict mode, any differences are failures
|
283
|
+
if @strictCallOrder
|
284
|
+
msg = "{Message}"
|
285
|
+
assert_block( msg ) {
|
286
|
+
unless diff.empty?
|
287
|
+
msg.replace __makeCallOrderFailMsg(*diff[0])
|
288
|
+
end
|
289
|
+
diff.empty?
|
290
|
+
}
|
291
|
+
|
292
|
+
# In non-strict mode, only methods missing (:-) from the call
|
293
|
+
# order are failures.
|
294
|
+
else
|
295
|
+
msg = "{Message}"
|
296
|
+
assert_block( msg ) {
|
297
|
+
missingDiff = diff.find {|d| d[0] == :-}
|
298
|
+
unless missingDiff.nil?
|
299
|
+
msg.replace __makeCallOrderFailMsg( *missingDiff )
|
300
|
+
end
|
301
|
+
missingDiff.nil?
|
302
|
+
}
|
303
|
+
end
|
304
|
+
end
|
305
|
+
alias_method :__verify, :verify
|
306
|
+
|
307
|
+
|
308
|
+
### Deactivate the object without doing call order checks and clear
|
309
|
+
### the call list, but keep its configuration.
|
310
|
+
def clear
|
311
|
+
@calls.clear
|
312
|
+
@activated = nil
|
313
|
+
end
|
314
|
+
alias_method :__clear, :clear
|
315
|
+
|
316
|
+
|
317
|
+
### Clear the call list and call order, unset any return values, and
|
318
|
+
### deactivate the object without checking for conformance to the
|
319
|
+
### call order.
|
320
|
+
def reset
|
321
|
+
self.__clear
|
322
|
+
@callOrder.clear
|
323
|
+
@returnValues.clear
|
324
|
+
end
|
325
|
+
alias_method :__reset, :reset
|
326
|
+
|
327
|
+
|
328
|
+
#########
|
329
|
+
protected
|
330
|
+
#########
|
331
|
+
|
332
|
+
### Register a call to the faked method designated by <tt>sym</tt>
|
333
|
+
### if the object is activated, and return a value as configured by
|
334
|
+
### #setReturnValues given the specified <tt>args</tt>.
|
335
|
+
def __mockRegisterCall( sym, *args, &block )
|
336
|
+
if @activated
|
337
|
+
@calls.push({
|
338
|
+
:method => sym,
|
339
|
+
:args => args,
|
340
|
+
:time => Time::now,
|
341
|
+
:caller => caller(2),
|
342
|
+
})
|
343
|
+
end
|
344
|
+
|
345
|
+
rval = @returnValues[ sym ]
|
346
|
+
case rval
|
347
|
+
when Method, Proc
|
348
|
+
return rval.call( *args, &block )
|
349
|
+
|
350
|
+
when Array
|
351
|
+
return rval.push( rval.shift )[-1]
|
352
|
+
|
353
|
+
when Hash
|
354
|
+
return rval[ args ]
|
355
|
+
|
356
|
+
else
|
357
|
+
return rval
|
358
|
+
end
|
359
|
+
end
|
360
|
+
alias_method :__mock_register_call, :__mockRegisterCall
|
361
|
+
|
362
|
+
|
363
|
+
### Build and return an error message for a call-order verification
|
364
|
+
### failure. The expected arguments are those returned in an element
|
365
|
+
### of the Array that is returned from Diff::diff.
|
366
|
+
def __makeCallOrderFailMsg( action, position, elements )
|
367
|
+
case action
|
368
|
+
|
369
|
+
# "Extra" method/s
|
370
|
+
when :+
|
371
|
+
extraCall = @calls[ position ]
|
372
|
+
return "Call order assertion failed: Unexpected method %s "\
|
373
|
+
"called from %s at %0.5f" %
|
374
|
+
[ extraCall[:method].inspect,
|
375
|
+
extraCall[:caller][0],
|
376
|
+
extraCall[:time] - @activated ]
|
377
|
+
|
378
|
+
when :-
|
379
|
+
# If there is a call in the position specified, it was a
|
380
|
+
# misordered or extra call. If not, there was a missing
|
381
|
+
# call.
|
382
|
+
missingCall = @callOrder[ position ]
|
383
|
+
if extraCall = @calls[ position ]
|
384
|
+
return "Call order assertion failed: Expected call to %s, "\
|
385
|
+
"but got call to %s from %s at %0.5f instead" %
|
386
|
+
[ missingCall.inspect,
|
387
|
+
extraCall[:method].inspect,
|
388
|
+
extraCall[:caller][0],
|
389
|
+
extraCall[:time] - @activated ]
|
390
|
+
else
|
391
|
+
return "Call order assertion failed: Missing call to %s." %
|
392
|
+
missingCall.inspect
|
393
|
+
end
|
394
|
+
|
395
|
+
else
|
396
|
+
return "Unknown diff action '#{action.inspect}'"
|
397
|
+
end
|
398
|
+
end
|
399
|
+
alias_method :__make_call_order_fail_msg, :__makeCallOrderFailMsg
|
400
|
+
|
401
|
+
|
402
|
+
end
|
403
|
+
|
404
|
+
|
405
|
+
### Factory method for creating semi-functional mock objects given the
|
406
|
+
### class which is to be mocked. It looks like a constant for purposes
|
407
|
+
### of syntactic sugar.
|
408
|
+
def self::MockObject( klass )
|
409
|
+
mockup = Class::new( Mockup )
|
410
|
+
mockup.instance_eval do @mockedClass = klass end
|
411
|
+
|
412
|
+
### Provide an accessor to class instance var that holds the class
|
413
|
+
### object we're faking
|
414
|
+
class << mockup
|
415
|
+
|
416
|
+
# The actual class being mocked
|
417
|
+
attr_reader :mockedClass
|
418
|
+
|
419
|
+
### Propagate the mocked class ivar to derivatives so it can be
|
420
|
+
### called like:
|
421
|
+
### class MockFoo < Test::Unit::MockObject( RealClass )
|
422
|
+
def inherited( subclass )
|
423
|
+
mc = self.mockedClass
|
424
|
+
subclass.instance_eval do @mockedClass = mc end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
# Build method definitions for all the mocked class's instance
|
429
|
+
# methods, as well as those given to it by its superclasses, since
|
430
|
+
# we're not really inheriting from it.
|
431
|
+
imethods = klass.instance_methods(true).collect {|name|
|
432
|
+
next if name =~ UnmockedMethods
|
433
|
+
|
434
|
+
# Figure out the argument list
|
435
|
+
argCount = klass.instance_method( name ).arity
|
436
|
+
optionalArgs = false
|
437
|
+
|
438
|
+
if argCount < 0
|
439
|
+
optionalArgs = true
|
440
|
+
argCount = (argCount+1).abs
|
441
|
+
end
|
442
|
+
|
443
|
+
args = []
|
444
|
+
argCount.times do |n| args << "arg#{n+1}" end
|
445
|
+
args << "*optionalArgs" if optionalArgs
|
446
|
+
|
447
|
+
# Build a method definition. Some methods need special
|
448
|
+
# declarations.
|
449
|
+
case name.intern
|
450
|
+
when :initialize
|
451
|
+
"def initialize( %s ) ; super ; end" % args.join(',')
|
452
|
+
|
453
|
+
else
|
454
|
+
"def %s( %s ) ; self.__mockRegisterCall(%s) ; end" %
|
455
|
+
[ name, args.join(','), [":#{name}", *args].join(',') ]
|
456
|
+
end
|
457
|
+
}
|
458
|
+
|
459
|
+
# Now add the instance methods to the mockup class
|
460
|
+
mockup.class_eval imethods.join( "\n" )
|
461
|
+
return mockup
|
462
|
+
end
|
463
|
+
|
464
|
+
end # module Unit
|
465
|
+
end # module Test
|
466
|
+
|
467
|
+
|