Unexpected invocation - cardinality

239 views
Skip to first unread message

Jeremy Mawson

unread,
Aug 24, 2008, 3:32:00 AM8/24/08
to instinct...@googlegroups.com
Hi

Can anyone help shed light on an issue I'm having?

I wanted to specify mock behaviour as:
one(serverMonitor).notify(with(aNonNull(InitialUpdateEvent.class)));

At compile time this is type checked and, as InitialUpdateEvent is an
implementation of UpdateEvent, it compiles against the signature
ServerMonitor.notify(UpdateEvent ue);

At runtime the value sent is ANYTHING. As a result I can get a false
positive if the subject calls notify with a different implementation of
UpdateEvent.

I've found this jMock thread where someone else has the same problem:
http://www.nabble.com/aNonNull-unexpected-behaviour-td17644154.html

Therein it is eventually suggested that a helper method be added to use
the matcher instanceOf and to cast it to the correct type. So I added a
helper method to do this:

import static org.hamcrest.core.IsInstanceOf.instanceOf;
...
private <T> T withAnInstanceOf(Class<T> aClass) {
return with((Matcher<T>) instanceOf(aClass));
}

My specification now reads:

expect.that(new Expectations() {{
one(serverMonitor)
.notify(withAnInstanceOf(InitialUpdateEvent.class));
}});

My test fails with the message:

Unexpected invocation. You may need to wrap the code in your new
Expections(){{}} block with cardinality constraints, one(), atLeast(), etc.
unexpected invocation:
serverMonitor.notify(<au.com.loftinspace.monci.monitor.event.InitialUpdateEvent@17d5d2a>)
no expectations specified: did you...
- forget to start an expectation with a cardinality clause?
- call a mocked method to specify the parameter of an expectation?
what happened before this:

serverMonitor.notify(<au.com.loftinspace.monci.monitor.event.InitialUpdateEvent@16fa474>)

Can someone point out what I doing wrong here?

Jem

Tom Adams

unread,
Aug 24, 2008, 8:19:19 PM8/24/08
to instinct...@googlegroups.com
Hey Jem,

I'll ask some dumb questions first, as I'm not sure what's happening:

- Is serverMonitor a mock? i.e. is it annotated with @Mock or created
using Mocker.mock()?

- Is ServerMonitor.notify() a static method? If so, it can't be mocked
out using jMock.

- Is your production code calling serverMonitor.notify()? The mock
code you've written expects that it will be called.

If you like, post the full example up on gist.github.com or pastie &
I'll take a closer look.

Cheers,
Tom

--
tom adams | e:tomjadams<at>gmail.com

Jem

unread,
Aug 25, 2008, 12:03:34 AM8/25/08
to instinct...@googlegroups.com
Hi Tom

Yes, it was annotated with @Mock. No, notify is an instance method. Yes, the prod code is calling serverMonitor.notify().

I'll publish the code tonight, as I need to get it from home.

Thanks again
Jem



2008/8/25 Tom Adams <tomj...@gmail.com>

Tom Adams

unread,
Aug 25, 2008, 1:11:20 AM8/25/08
to instinct...@googlegroups.com
np, we'll see how we go :)

Tom


On 25/08/2008, at 2:03 PM, Jem wrote:

> Hi Tom
>
> Yes, it was annotated with @Mock. No, notify is an instance method.
> Yes, the prod code is calling serverMonitor.notify().
>
> I'll publish the code tonight, as I need to get it from home.
>
> Thanks again
> Jem

--

Jeremy Mawson

unread,
Aug 25, 2008, 7:13:22 AM8/25/08
to instinct...@googlegroups.com
Hi Tom

Well, good news. It now works, though I didn't change anything since
yesterday, which makes me nervous. I've pasted an example regardless to
be clear on what I was talking about.

An example of a false positive, because aNonNull does not assert runtime
types: http://gist.github.com/7060

The same test, now failing (as it should) after including the fix
suggested by
http://www.nabble.com/aNonNull-unexpected-behaviour-td17644154.html :
http://gist.github.com/7061

Cheers
Jem

Tom Adams

unread,
Aug 25, 2008, 6:16:32 PM8/25/08
to instinct...@googlegroups.com
Hey Jem,

It's good to see you've got it fixed, or, sorted at least, some quick
comments...

You may be running into an issue with the way the generics have been
coded with Hamcrest or jMock, without digging into the code I'm not
sure if what you've found is expected or not.
You should be able to inline your matcher:

private <T> T withAnInstanceOf(Class<T> aClass) {
return with((Matcher<T>) instanceOf(aClass));
}


so that it becomes something like:


expect.that(new Expectations() {{
one(mockListUser).useAList(with((Matcher<T>)
instanceOf(ArrayList.class)));
}}


However Java's type inferencer may get in your way. This will make
your tests easier to read.

Also, you might want to create a raw jMock example that shows this
(i.e. without Instinct, just using jMock's JUnit bindings) and post it
to the jMock list. They're always happy to fix problems like this, or,
they may tell you it's a feature :)

The other issue you'll be having is that in Java there's no way to
mock out a call to new. This can become a problem if you want to be
able to ensure that objects get passed around correctly. Once approach
to this is to create a factory, with a method on it, that creates the
object for you, you can then expect a call to the factory. If you take
this to the extreme, you get a generic type-safe factory, Instinct has
one called ObjectFactory.

Here's an example: http://gist.github.com/7169

However, I think this is bad use of mocking, it creates tight tests
and it doesn't add any value. I'd say doing this is something to be
avoided.

Cheers,
Tom

--

Reply all
Reply to author
Forward
0 new messages