Background:
This is a JMock+JUnit specific question (those are the two technologies I must use). Yes, what I want to do can be done with PowerMock, but this is an edge case that doesn't warrant changing of tools. And no, sorry, I'm not asking this question to debate the philosophical validity of static methods :)
With that out of the way, I will really thank anyone taking a look at this question.
Question:
I have piece of legacy code that I need to write a test for (we are trying to put tests around inherited code to ensure we don't break anything during a potentially massive refactoring effort... that's a tale for another time.)
Goal:
The method I'm trying to mock is the Foo.bar
method in the class below using JMock's class imposterizer facility (via JUnit4Mockery.)
The code below is representative to the code I'm test-wrapping:
public class Foo {
public abstract <T> void bar(
Class<? extends T> paramClass, T paramT);
My test setup aims to allow any # of calls to bar()
receiving a Class instance (which obviously degenerates to Class ... silly Java type erasure "feature"), paired with any instance of Snafu.
That is the key distinction here. I'm not pairing two Matcher parameters, or two literal parameters, but one literal (T.class) and any value of type T. JMock does not allow this, so the expected solution would be to have a Matcher> and a Matcher:
Foo mock = context.mock(Foo.class);
context.checking(new Expectations() {
// keep warnings close to the culprit code when possible
@SuppressWarnings("unchecked")
public void allow(final Foo mockedFoo) {
allowing(mockedFoo).bar(
with(any(Snafu.class.getClass())), // Matcher that *should* resolve to Class<?>
with(any(Snafu.class))); // matcher to anything of type Snafu.class
}
{
allow(mockedFoo);
}
});
Then, we inject the mocked Foo, which eventually gets called like this by another class, which I'll refer to as the Driver
(*I'll get back to the static method call later):
// fooImpl has been replaced/injected with our mock
fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
Problem:
The problem is that when the Driver
invokes the bar
method on the mocked Foo
instance, my test encounters the following exception:
java.lang.IllegalArgumentException: not all parameters were given explicit matchers: either all parameters must be specified by matchers or all must be specified by values, *you cannot mix matchers and values*
at org.jmock.internal.InvocationExpectationBuilder.checkParameterMatcherCount(InvocationExpectationBuilder.java:98)
at org.jmock.internal.InvocationExpectationBuilder.createExpectationFrom(InvocationExpectationBuilder.java:91)
at org.jmock.internal.InvocationToExpectationTranslator.invoke(InvocationToExpectationTranslator.java:19)
at org.jmock.internal.FakeObjectMethods.invoke(FakeObjectMethods.java:38)
at org.jmock.lib.legacy.ClassImposteriser$4.invoke(ClassImposteriser.java:129)
at .....
Apparently (or so it looks to me), JMock matchers' see Class
instances as values, regardless of how we try to match them. Or am I missing something?
I'm encountering similar exceptions in many legacy calls that take a java.lang.Class
argument. Obviously, anything that looks like X.class
will be a value, not a new instance.
But therein lies the problem because the other argument must be resolved with a matcher, not just with an actual value.
[*] Ideally one could rewrite the static method call in
fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
with something more amenable to mocking (a non-static method, another object or something injected with a IoC).
Probably that is the way we will eventually go, but for the time being, the code in question has a significant number of static calls.
I would like to defer that till a more appropriate moment, and instead find a general JMock solution, if one exists, that allows me to set the necessary expectations of mocking functions that like Foo.bar
above.
fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
with thecontext.checking
method provided by you didn't throw any exceptions (but I didn't really know where you got thebroker
argument from)... – Thadeusbroker
part is a copy/paste mistake. Instead ofallow(broker)
, it should beallow(mockedFoo)
. I'll extract code out to make a minimum compilable proof-of-concept to show the error. BTW, the code as-is compiles. It is at run-time that we get the exception. – Eglantine