Dynamic mock object anyone?


#1

Hi guys, got this problem with creating such a thing… hope anyone
could help…

the problem:

ok, now i have this mock object that would simulate a external rpc call.
eg

require ‘models/xmlrpc_agent’

class XmlrpcAgent

def create(params)
200
end
end

but the value of the value returned is fixed. which is quite hard for me
to test the controller when different values are returned from the rpc
call… is there a way to make the number returned dynamic?? Dynamic in
the sense that i can pre-define the returned number in the functional
test case to simulate the different kind of situation that might
arise…

-burninglegion


#2

On Apr 19, 2006, at 9:19 PM, bao lee wrote:

class XmlrpcAgent

def create(params)
200
end
end

This is a stub, not a mock.

but the value of the value returned is fixed. which is quite hard
for me
to test the controller when different values are returned from the rpc
call… is there a way to make the number returned dynamic??
Dynamic in
the sense that i can pre-define the returned number in the functional
test case to simulate the different kind of situation that might
arise…

I use the following pattern when I use stubs, taking advantage of
Ruby’s open classes. My flickr interface use open-uri to do its
work, so I inject my own open that will be called before Kernel’s. I
record the passed URIs and give the positional response. This makes
it easy to verify the method DTRT.

class Flickr

attr_accessor :responses, :uris

def open(uri)
@uris << uri
yield StringIO.new(@responses.shift)
end

end

class FlickrTest < Test::Unit::TestCase

#…

def test_find_email
@flickr.responses << <<-EOF

<?xml version="1.0" encoding="utf-8" ?> drbrain EOF
 nsid = @flickr.find_email :find_email => 'removed_email_address@domain.invalid'

 assert_equal 1, @flickr.uris.length
 assert_equal 'http://flickr.com/services/rest/?

api_key=API_KEY&find_email=dr
brain%40segment7.net&method=flickr.people.findByEmail’,
@flickr.uris.first

 assert_equal '50178138@N00', nsid

end

end


Eric H. - removed_email_address@domain.invalid - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com


#3

Hi eric,

sory for the confusion which i first came upon… the ruby on rails book
didnt tell me much about stubs and also the differences between the
2…

ok so after reading thorugh the code i got a rough idea about it but it
seems to be a bit complicated…btw does this apply to functional test
also??


#4

Use FlexMock, quite simple and powerful.


#5

Use FlexMock, quite simple and powerful.

Thanks for the pointer


#6

I wrote an article about selectively overriding methods in particular
instances of objects for testing - I called them “Partial Mock
Objects”.

http://karmiccoding.com/articles/2006/03/11/under-the-hood-with-ruby-partial-mock-objects-for-unit-testing

Cheers,

-David F.


#7

David F. wrote:

I wrote an article about selectively overriding methods in particular
instances of objects for testing - I called them “Partial Mock
Objects”.

http://karmiccoding.com/articles/2006/03/11/under-the-hood-with-ruby-partial-mock-objects-for-unit-testing

Cheers,

-David F.

yeah i have seen that post… great post…


#8

bao lee wrote:

David F. wrote:

I wrote an article about selectively overriding methods in particular
instances of objects for testing - I called them “Partial Mock
Objects”.

http://karmiccoding.com/articles/2006/03/11/under-the-hood-with-ruby-partial-mock-objects-for-unit-testing

Cheers,

-David F.

yeah i have seen that post… great post…

btw i think that that post only works with instances of objects… issit
possible the change the class itself rather than instance of the class?

cos my problem is the stub that i used is to replace something in the
controller algorithm and i cant change it within the controller… so it
does not work when i try
override_method(XmlrpcAgent,:create) { 502 }
where XmlrpcAgent is the class name and :create is the name of the
function…

is there any tweaks to the code you have done that could allow that to
happen??

thanks for you help


#9

bao lee wrote:

hey David F. , i think i have found what i wanted… from your
code :

def override_method(obj,method_name,&block)
#klass = class <<obj; self; end
#klass.send( :undef_method , method_name )
#klass.send( :define_method, method_name, block)
obj.send(:undef_method,method_name)
obj.send(:define_method,method_name,block)
end

the one commented is the your original code, the one uncommented is the
one i wrote… this way the class method is changed completely and my
testing went as smooth as it can get!!! thanks for the enlightenment
provided!!


#10

bao lee wrote:

def override_method(obj,method_name,&block)
#klass = class <<obj; self; end
#klass.send( :undef_method , method_name )
#klass.send( :define_method, method_name, block)
obj.send(:undef_method,method_name)
obj.send(:define_method,method_name,block)
end

ok… i found a disadvantage when doing this way…that is when writing
my tests, when i called this override method… the next test that used
the overrided class method will not be revert back automatically…eg.
for the class in test/mock/test :
class XmlrpcAgent

def create(params)
200
end
end

in test_controller :
if i called override_method(XmlrpcAgent,:create){ 502 } in one of my
test case

then the next test case that used XmlrpcAgent class will now return 502
rather than 200…
so the next test case that need to use XmlrpcAgent will have to override
the method to its own use… well a bit of hard coding here… but i
think that would be the best solution i could think of currently…


#11

David F. wrote:

Hey there,

Yeah, the aim of the code provided is to only override the method
specified in a particular instance of the object. If you want to do
it for the entire class, what you have there will work, but as you’ve
seen it will carry through for the subsequent tests. If you want to
revert it back, you would have to put something in your test’s
‘teardown’ method to bring it back…

Have a look at the ‘alias_method’ call in the Ruby API - instead of
using ‘undefine_method’ you could alias the method being overriden to
another same ("#{method_name}_old", for example) and in the teardown
you could alias it back. I think that would work.

Cheers!

-David F.

hmmm… interesting… ok i will go and take a look about that and post
any updates i have…
thanks !!


#12

Hey there,

Yeah, the aim of the code provided is to only override the method
specified in a particular instance of the object. If you want to do
it for the entire class, what you have there will work, but as you’ve
seen it will carry through for the subsequent tests. If you want to
revert it back, you would have to put something in your test’s
‘teardown’ method to bring it back…

Have a look at the ‘alias_method’ call in the Ruby API - instead of
using ‘undefine_method’ you could alias the method being overriden to
another same ("#{method_name}_old", for example) and in the teardown
you could alias it back. I think that would work.

Cheers!

-David F.