Delay EasyMock verification
I'm using EasyMock to create mock objects for JUnit testing in Java. I create a mock object and pass it to another thread where it expects methods to be called. In the other thread, the calls are enclosed in a try/catch(Throwable)
block, so when an unexpected call occurs on the mock and it thus throws AssertionError
, that error is caught by the catch block and treated.开发者_JAVA百科 So, even though an unexpected call occurred, the test passes.
In order to have the test fail as expected, I would like to delay all verification of calls to the EasyMock.verify(mock)
call made in the test-runner thread at the end. Is this possible and how?
I'm not sure about how to do this with EasyMock, but this behavior is possible with Mockito because verification assertions can be specified at the end of the test.
The correct solution I'd guess is to stop catching Throwable
. Doing so catches all Error
s as you're finding, which can be quite dangerous... are you absolutely positively 100% sure you need to catch Throwable
? Why?
(If it turns out you do, you could catch AssertionError
specifically and rethrow it. But that's ugly!)
Try using nice mocks:
http://easymock.org/EasyMock2_5_2_Documentation.html
"Nice Mocks On a Mock Object returned by createMock() the default behavior for all methods is to throw an AssertionError for all unexpected method calls. If you would like a "nice" Mock Object that by default allows all method calls and returns appropriate empty values (0, null or false), use createNiceMock() instead. "
Default values will be returned for unexpected calls instead of throwing AssertionError, but you can still verify them with the verify() method (in which case the AssertionErrors will be thrown)
As @deterb suggested, it's possible with Mockito but you have to know the method name or you have to set expectations for every method. Here is an example:
The mocked interface:
public interface MyInterface {
void allowedMethod();
void disallowedMethod();
}
The user class which catches AssertionError
:
public class UserClass {
public UserClass() {
}
public static void throwableCatcher(final MyInterface myInterface) {
try {
myInterface.allowedMethod();
myInterface.disallowedMethod();
} catch (final Throwable t) {
System.out.println("Catched throwable: " + t.getMessage());
}
}
}
And the Mockito test:
@Test
public void testMockito() throws Exception {
final MyInterface myInterface = mock(MyInterface.class);
UserClass.throwableCatcher(myInterface);
verify(myInterface, never()).disallowedMethod(); // fails here
}
The same is possible with EasyMock but it needs some work:
@Test
public void testEasyMock() throws Exception {
final AtomicBoolean called = new AtomicBoolean();
final MyInterface myInterface = createMock(MyInterface.class);
myInterface.allowedMethod();
myInterface.disallowedMethod();
final IAnswer<? extends Object> answer = new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
System.out.println("answer");
called.set(true);
throw new AssertionError("should not call");
}
};
expectLastCall().andAnswer(answer).anyTimes();
replay(myInterface);
UserClass.throwableCatcher(myInterface);
verify(myInterface);
assertFalse("called", called.get()); // fails here
}
Unfortunately you also have to know the method names here and you have to define expectations like myInterface.disallowedMethod()
and expectLastCall().andAnswer(answer).anyTimes()
.
Another possibility is creating a proxy with the Proxy
class (with a custom InvocationHandler
) and using it as a mock object. It definitely needs more work but it could be the most customizable solution.
Finally don't forget that it's also possible to create a custom implementation with or without delegation to the EasyMock mock object. Here is one with delegation:
public class MockedMyInterface implements MyInterface {
private final MyInterface delegate;
private final AtomicBoolean called = new AtomicBoolean();
public MockedMyInterface(final MyInterface delegate) {
this.delegate = delegate;
}
@Override
public void allowedMethod() {
delegate.allowedMethod();
}
@Override
public void disallowedMethod() {
called.set(true);
throw new AssertionError("should not call");
}
public boolean isCalled() {
return called.get();
}
}
And the test for it:
@Test
public void testEasyMockWithCustomClass() throws Exception {
final MyInterface myInterface = createMock(MyInterface.class);
myInterface.allowedMethod();
final MockedMyInterface mockedMyInterface =
new MockedMyInterface(myInterface);
replay(myInterface);
UserClass.throwableCatcher(mockedMyInterface);
verify(myInterface);
assertFalse("called", mockedMyInterface.isCalled()); // fails here
}
精彩评论