About stubs

10 views
Skip to first unread message

Johnny

unread,
May 11, 2009, 12:54:59 AM5/11/09
to gmock-dev
After reading Martin's article "Mocks aren't
Stubs"(http://martinfowler.com/articles/mocksArentStubs.html), I
realized what the stubs should look like. It is exactly recording
arguments. So, I have some ideas of stubs:

1. for not confusing the stubs, I suggest to rename the current stub()
method to anyTimes().

2. DSL for stubs:

def mailer = stub(MailService) // stubs are nice, i.e., if no result is
set for a method, a default null value
will be returned

mailer.toString.returns('mailer') // any method named 'toString' will
return 'mailer', no matter what
params are passed in. BUT it is
inconsistent with the property
mocking, it may need more discuss
mailer.reset.raises(RuntimeException) // methods named 'reset' will
throw an exception
mailer.address.getter.returns('some address') // getter of address will
return 'some address'
mailer.send.params('message', 'user') // name the params of send
mailer.receive.params(1: 'timeout') // name the second param of receive
mailer.close().returns('success') // the close method without params
will return 'success'

play {
2.times { mailer.send 'test' }
mailer.address = 'some address'
def address = mailer.address
}

assert mailer.send.called // 'called' returns the times of call of the
method
assert mailer.send.called == 2
assert mailer.send[0].params[0] == 'test' // the first param of the
first call of 'send'
assert mailer.send[1].params.message == 'test' // the param named
'message' of the
second call of 'send'
assert mailer.send[1].message == 'test' // shortcut of 'params.message'
assert mailer.send('test').called // get the times of call with
specified params

assert mailer.address.setter.called // get the times of call of setter
assert mailer.address.setter[0].value == 'some address' // the value of
the first
call of the
setter
assert mailer.address.set('some address').called in 1..2 // setter with
specified
value

assert mailer.address.getter.called < 2 // times of call of getter

Julien

unread,
May 14, 2009, 3:10:03 AM5/14/09
to gmock-dev
This is great idea and I think I learn a bit about stub through the
Martin Fowler article and your example.

1. If we change stub to anyTime we will still have to support the stub
version for a long time, long time enough that it might stay forever.
But why not.

2. I like it.

My main concern is the inconsistency with the method mocking. We need
it to look like a method if we want people to understand it. We also
need to keep the property still for property stubbing.

What about:
mailer.reset( _ ).raises(RuntimeException)
For all methods call reset. The underscore method will return us
something similar to the contructor() method.

Now we can use the normal property for stubbing.
mailer.address = 'some address' // getter of address will return 'some
address'
I think we should let property behave like normal property. Which mean
that if we set its value within the play closure then the getter will
change accordingly. We just need a way to retrieve the state the stub
has gone through.

I am not sure about the benefit of setting expectation on a single
params. But we could do it like that:
mailer.receive( _, 'timeout') ...

We need a bit more thinking around it but I like the general idea. It
look like adding a bit of mockito into gmock or should I say stubito.

Johnny

unread,
May 15, 2009, 7:56:20 AM5/15/09
to gmoc...@googlegroups.com
1. I had thought of the underscore a lot before, but I thought it is
very inconvenient to type "(_)", you have to press 'shift', '9', '-' and
'0', and it looks ugly. Nevertheless, I have not found a better way yet.

2. OK for the property stubbing. But we should think of the verification
syntax of property stubbing and method stubbing:

assert mailer.property == 1
assert mailer.method[0].params[0] == 2

Here mailer.method[0] is actually mailer.method.get(0). So, how can we
tell the property of a stub (mailer in this case) should return a real
property, or something we can call 'get(0)' on?

3. mailer.receive.params(1: 'timeout') doesn't set expectations on the
receive() method, it just names the second param of the receive()
method (maybe paramNames() is more clear). So if you call

mailer.receive('test', 30)

in the play closure, then you can assert like:

assert mailer.receive[0].params.timeout == 30

在 2009-05-14四的 00:10 -0700,Julien写道:

Julien

unread,
May 16, 2009, 5:59:59 AM5/16/09
to gmock-dev
I am not convinced anymore by the "_" syntax. We are trying to be able
to return a different object based on the parameter passed to the
method. For most usages of stub we need to return the same object
whatever has been passed to the method, we can do it this way:

def ourStub = stub()
ourStub.method().returns("foo")

play {
assertEquals "foo", ourStub.method()
// or
assertEquals "foo", ourStub.method("something", "else")
}

We then can verify the call count and the argument the way you suggest
(slightly modified)
assertEquals 2, ourStub.method().called
assertEquals [], ourStub.method()[0]
assertEquals ["something", "else"], ourStub.method()[1]

The array form of accessing of the method return the arguments passed
to it. This mean that we don't have to give the ability of naming the
params which keep everything simple.

Now if we want to return different object based on the arguments we
just set up different expectation:
ourStub.method().returns("not found") //default
ourStub.method("user1").returns("johnny")
ourStub.method("user2").returns("julien")

The first expectation set up the return value for the method called
with no arguments and for the call that didn't match the two other one
(with "user1" and "user2").

You might say that there is a problem here. In the clear() method
example when clear is meant to be called with no arguments this will
define as a side effect a default return value if clear is called with
different args. In this rare scenario the user would have to define
explicitly the return value for method calls with different argument.
I think that this type of scenario are extremely rare in practice and
that we should live with it.

Regarding property I think we could solve the dsl issue like this:
def ourStub = stub()
ourStub.name = "user1"
play {
assertEquals "user1", ourStub.name
assertEquals "user1", ourStub.name
ourStub.name = "user2"
assertEquals "user2", ourStub.name
}
// We could then verify it this way:
assertEquals 2, ourStub.name.size() // name went through 2 different
value
assertEquals "user1", ourStub.name[0].value
assertEquals 2, ourStub.name[0].called
assertEquals "user2", ourStub.name[1].value
assertEquals 1, ourStub.name[1].called

I think this would work. When we are not in the play closure the
getter of a property return one of our object which we can use.

Looking forward to hear your thought about it.

Johnny

unread,
May 16, 2009, 10:43:21 AM5/16/09
to gmoc...@googlegroups.com
OK for the method stubbing.

Actually, you have solved the dsl issue by changing

assert stub.method[0].called == 2

to

assert stub.method()[0].called == 2

Then method stubbing and property stubbing are distinguishable.

However, I don't agree on the new property verification dsl like

assert stub.name[1].value == 'user2'

I think we should just allow verifying the final state like

assert stub.name == 'user2'

Verifying some intermediate states sounds like behavior verification to
me, which should be done via mocks.

在 2009-05-16六的 02:59 -0700,Julien写道:

Julien

unread,
May 18, 2009, 2:00:13 AM5/18/09
to gmock-dev
I agree with you and the property stubbing. As soon as we can verify
the end result then it should be enough.
Reply all
Reply to author
Forward
0 new messages