I put a simple retry because the operation can rarely fail. The simplified code is below. The method putObject
can accidentally throw an exception, in this case the retry should allow to invoke this method again. Is it possible to write a JUnit test for this?
I know that with Mockito library we can force to throw an Exception invoking a method but how to force this exception to be thrown only once?
public class RetryExample {
Bucket bucket = new Bucket();
static int INTERNAL_EXCEPTION_CODE = 100;
class AException extends RuntimeException {
private static final long serialVersionUID = 1L;
int statusCode;
public int getStatusCode() {
return statusCode;
}
}
class Bucket {
public void putObject(String fileName, byte[] data) throws AException {
System.out.println("PutObject=" + fileName + " data=" + data);
}
}
public void process(String fileName, byte[] data) throws AException {
try {
retryOperation((f, d) -> bucket.putObject(f, d), fileName, data);
} catch (Exception ex) {
throw new AException("Failed to write data", ex);
}
}
private <T, U> void retryOperation(BiConsumer<T, U> biConsumer, T t, U u) {
int retries = 0;
boolean retry = false;
AException lastServiceException = null;
do {
try {
biConsumer.accept(t, u);
} catch (AException e) {
lastServiceException = e;
int statusCode = e.getStatusCode();
if (statusCode == INTERNAL_EXCEPTION_CODE) {
throw e;
} else {
break;
}
}
retries++;
if (retries >= 3) {
retry = false;
}
} while (retry);
if (lastServiceException != null) {
throw lastServiceException;
}
}
Test Class:
public class RetryExampleTest {
...
@Test
public void test() {
RetryExample retryExample = new RetryExample();
String fileName = "TestFile";
byte[] data = simulatedPayload(10000);
try {
retryExample.process(fileName, data);
} catch (Exception e) {
fail("Exception thrown=" + e);
}
}
In order to test the exception thrown by any method in JUnit 4, you need to use @Test(expected=IllegalArgumentException. class) annotation. You can replace IllegalArgumentException. class with any other exception e.g. NullPointerException.
If you want to ignore a test method, use @Ignore along with @Test annotation. If you want to ignore all the tests of class, use @Ignore annotation at the class level.
According to the Mockito documentation you can set different behavior for consecutive method calls.
when(mock.someMethod("some arg"))
.thenThrow(new RuntimeException())
.thenReturn("foo");
In case of a void method you can do something similar (Mockito documentation)
doThrow(new RuntimeException())
.doNothing()
.when(mock).doSomething();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With