Library for Mocking

T

Thiago Arrais

Rubysts,

I am looking for a library for mocking objects.

I am aware of FlexMock and Test::Unit::Mock (seems like RubyMock
turned into Test::Unit::Mock, am I right?), but what I am looking for
would be something more like a hybrid of the two. Maybe I am really
misguided here, but this are my (admitedly first) impressions about
both.

FlexMock works by creating a full-blown, ready-to-use, suited-to-your
needs mock object in one method call (namely FlexMock.use). Then you
can just inject it into your tested object and use it.

Test:Unit::Mock mocks, on the other side, are created with (almost) no
idea of what will they be used for. Instead, they are born in a state
in which they still need to be told what they should expect. After you
have told them everything you want to happen to them (and how they
should respond), you switch them to replay state by calling the
#activate method. With FlexMock, there is no need to switch states,
because all the setup is done before the object is created. It is like
the mock is already born in replay state.

My main complaint about Test::Unit::Mock is about the big verbose
setup phase. With it you need to call a number of 'set' methods before
doing your stuff. In my opinion, this setup code can get pretty big,
pretty fast. For instance, let's take the example from
http://www.deveiate.org/projects/Test-Unit-Mock/wiki/Examples

Here we want to write a mock for a TCP socket. We want its 'addr'
method to return one of three values on each consecutive call, so we
write

mockSocket.setReturnValues( :addr =3D> [
["AF_INET", 23, "localhost", "127.0.0.1"],
["AF_INET", 80, "slashdot.org", "66.35.250.150"],
["AF_INET", 2401, "helium.ruby-lang.org", "210.251.121.214"],
] )

After setting up the return values for the other methods, we specify
the call order with
'setCallOrder'

mockSocket.setCallOrder( :addr, :getsockopt, :write, :read,
:write, :read )

I like the idea of the setup phase. I just don't like the particular
way it is done in Test::Unit::Mock. I prefer the record/playback
metaphor, where you actually call the methods you want to be called on
the record phase and then the object under test is responsible for
making the expected calls on playback phase. For instance, I would
like to write something like this:

# mockSocket starts in record state

mockSocket.addr do
return "AF_INET", 23, "localhost", "127.0.0.1"
end

mockSocket.getsockopt do
# getsockopt return stuff here
end

# we switch it to playback state here
mockSocket.activate

Is there already anything along these lines? Am I totally lost because
the libs I just listed already do that? Any ideas?

Cheers,

Thiago Arrais
 
J

Jim Weirich

Thiago said:
Rubysts,

I am looking for a library for mocking objects. [...]
FlexMock works by creating a full-blown, ready-to-use, suited-to-your
needs mock object in one method call (namely FlexMock.use). Then you
can just inject it into your tested object and use it.

Actually, you still need to specify the behavior of the mock. The "use"
module method just ensures that that mock_verify is properly called at
the end of the test.

FlexMock currently has two ways of specifying behavior: (1) the
original "mock_handles" way and (2) the JMock-like "should_receive" way.
[...] I prefer the record/playback
metaphor, where you actually call the methods you want to be called on
the record phase and then the object under test is responsible for
making the expected calls on playback phase.

Currently, FlexMock does not support a recording mode. But one could
easily be added:

class FlexMock
class Recorder
def initialize(mock)
@mock = mock
end
def method_missing(sym, *args, &block)
@mock.should_receive(sym).and_return(&block)
end
end

def should_expect
yield Recorder.new(self)
end
end

This allows you to do the following:

FlexMock.use("socket") do |mock_socket|
mock_socket.should_expect do |recording_socket|
recording_socket.addr { ["AF_INET", 23, "localhost", "127.0.0.1"]
}
recording_socket.getsocketopt { DEFAULT_SOCKET_OPTS }
end

# Testing code goes here that uses mock_socket and
# calls addr and getsocketopt.
end

There are some open questions.

(1) how are arguments to the mock method validated? (exact match,
constraints, no validation?)

(2) is the order of the messages important?

The implementation provided above does no argument validation (but the
arguments are sent to the recording block for hand validation) and the
order of method calls is ignored. However, I think automatic parameter
validation could be added without too much effort.

If there is sufficient interest in a recording interface to MockFlex, I
am willing to consider it for inclusion in the library.
 
J

Jim Weirich

Jim Weirich wrote:
[...]
However, I think automatic parameter
validation could be added without too much effort.

Ha! I now know the exact amount of effort to add parameter validation
... Change the method_missing definition in Recorder to read:

def method_missing(sym, *args, &block)
@mock.should_receive(sym).and_return(&block).with(*args)
end

(i.e. add ".with(*args)" to the end of the line).

Not only do you get parameter validation, but the full range of
constraints available to the "should_receive" sytle of specification.

For example:

FlexMock.use do |mock|
mock.should_expect do |recorder|
recorder.add(Integer, 4) { |i, f|
i + f
}.at_least.once
end

# Test fails if mock.add is not called at least once with
# integer for the first argument and the number 4 for the
# second argument.
end
 
T

Thiago Arrais

Actually, you still need to specify the behavior of the mock. The "use"
module method just ensures that that mock_verify is properly called at
the end of the test.

Hmm... I knew I was wrong at some point. I think I got fooled by the
docs at http://onestepback.org/software/flexmock/

Somewhere in the simple example, you can read

FlexMock.use("temp") do |sensor|
sensor =3D FlexMock.new
sensor.should_receive:)read_temperature).and_return { readings.shift }
sampler =3D TemperatureSampler.new(sensor)
assert_equal 12, sampler.average_temp

Because of indentation it seems that the "should_receive" methods
(i.e. the setup phase) go inside the block that gets passed to the use
method and the stimuling calls go outside. In that sense, after the
use method returns, the mock is ready for usage. But now I can see
they are really at the same level and the setup phase really precedes
the usage phase as multiple calls and not one method call. I don't
know if I made myself clear, but that was pretty much my previous
understanding.
(1) how are arguments to the mock method validated? (exact match,
constraints, no validation?)

Well, you pretty much answered this one yourself on you next post :)
(2) is the order of the messages important?

At least in my original example it is. However, I see that we may
often need to specify that order is not important, specially when we
use the repetition modifiers (like once and twice). I definitely need
some experimenting in order to clear my mind on this topic. How does
JMock handles that?
If there is sufficient interest in a recording interface to MockFlex, I
am willing to consider it for inclusion in the library.

You can find at least one person. Anyway, I know I am patching my
MockFlex (or is it called FlexMock?).

Cheers,

Thiago Arrais
 
J

Jim Weirich

Thiago said:
Hmm... I knew I was wrong at some point. I think I got fooled by the
docs at http://onestepback.org/software/flexmock/

Somewhere in the simple example, you can read

FlexMock.use("temp") do |sensor|
sensor = FlexMock.new
sensor.should_receive:)read_temperature).and_return { readings.shift
}
sampler = TemperatureSampler.new(sensor)
assert_equal 12, sampler.average_temp

Because of indentation it seems that the "should_receive" methods
(i.e. the setup phase) go inside the block that gets passed to the use
method and the stimuling calls go outside.

Gah! That example is all messed up. It looks like I might have been
interupted while editting it and never got back to cleaning it up. I
will fix this ASAP.

Sorry for the confusion. Its not a good thing when your first example
is garbled.
At least in my original example it is. However, I see that we may
often need to specify that order is not important, specially when we
use the repetition modifiers (like once and twice). [...]

Ordering issues was one of the driving forces that lead to the creation
of FlexMock. At the time, the mocking framework I was using was too
inflexible on the allowed order of received messages. Requiring the
exact order was, in my mind, an overspecification of the behavior I
wished to implement. I'm pretty happy with the ordering specification
in FlexMock at the moment.

One of the advantages of a recording mode for mocks is that you can give
the recorder to a known good algorithm and record the actual calls as
expectations. Then give the mock to a reimplemenation of the known
good algorithm and can check that the new algorithm exactly matches the
old. Obviously, this only works for imperative interfaces (ie. no
queries, because the recorder wont return the proper values). But for
that limited range of use, it is pretty handy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,954
Messages
2,570,114
Members
46,702
Latest member
VernitaGow

Latest Threads

Top