I have class with a forwarding method foo
:
void foo( Concrete c, String s ) { c.bar( s ); }
I wish to test whether foo
does, in fact, forward. Unfortunately for me, Concrete
is a class in a third-party library, and is a concrete type, not an interface. Thus I must use ClassImposteriser
in JMock to mock Concrete
, so in my test case, I do this:
@Test
public final void testFoo() {
Mockery context = new JUnit4Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
final Concrete c = context.mock(Concrete.class);
final String s = "xxx" ;
// expectations
context.checking(new Expectations() {{
oneOf (c).bar(s); // exception gets thrown from here
}});
new ClassUnderTest.foo( c, s );
context.assertIsSatisfied();
}
Unfortunately, Concrete.bar
in turn calls a method that throws. That method is final, so I can't override it. Further, even if I comment out the line new ClassUnderTest.foo( c, s );
, the exception is thrown when JMock sets up exceptions, not when foo
is called.
So how can I test that method ClassUnderTest.foo
does forward to Concrete.bar
?
Edit:
Yes, bar is final.
My solution, which is not a general one, was to use a "Tester" class in the third-party library to correctly set up Concrete
.
It's not clear from the question text if Concrete.bar() is final or if Concrete.somethingElse() is final and called from Concrete.bar().
If Concrete.bar() is not final, create a hand-written stub for Concrete like this:
public class ConcreteStub extends Concrete
{
public int numCallsToBar = 0;
@Override
public void bar(String s) { numCallsToBar++; }
}
and in your test code:
ConcreteStub c = new ConcreteStub();
foo(c,"abc");
assertEquals(1,c.numCallsToBar);
If Concrete.bar() is final, it is more complicated and the answer depends on the complexity of Concrete and your project's use of the Concrete class. If your use of Concrete is simple enough, I would consider wrapping Concrete in an interface (Adapter Pattern) that you can then mock out easier.
Benefits to the Adapter Pattern solution: Possibly clarify behavior by naming interface after your project's use of Concrete. Easier to test.
Drawbacks to the Adapter Pattern solution: Introduces more classes with possibly little benefit to production code. I don't know what Concrete does and it may not be practical to wrap Concrete in an interface.