Exception handling is an important part of Java coding. The Java community follows a set of best practices about exception handling. The following are the exception handling best practices for unit testing:
Calculator
program has a divide
method. It takes two integers, divides, and returns a result. When divide
encounters a divide by zero, the program should throw an exception. The following is the code:public class Calculator { public int divide(int op1, int op2) { return op1/op2; } }
The following is the test:
@Test public void divideByZero_throws_exception() throws Exception { try { calc.divide(1, 0); fail("Should not reach here"); } catch (ArithmeticException e) { } }
Instead of catching ArithmeticException
, we can apply the JUnit 4 pattern as follows:
@Test(expected = ArithmeticException.class) public void divideByZero_throws_exception() throws Exception { calc.divide(1, 0); }
A more elegant way is to check the ExpectedException
rule. The following is the modified test with ExpectedException
:
public class CalculatorTest { @Rule public ExpectedException expectedException= ExpectedException.none(); Calculator calc = new Calculator(); @Test public void divideByZero_throws_exception(){ expectedException.expect(ArithmeticException.class); expectedException.expectMessage("/ by zero"); calc.divide(1, 0); } }
ExpectedException
expects an exception and an error message. If the exception is not thrown, or the message doesn't match, the test fails.
@Test public void fails_when_an_exception_is_thrown() { try { calc.divide(1, 0); }catch(Exception ex) { fail("Should not throw an exception"); } }
Instead, just write the following lines. The test will fail automatically if any exception is thrown:
@Test public void fails_when_an_exception_is_thrown() { calc.divide(1, 0); }
ArithmeticException
and sets a Boolean flag, and finally asserts the flag. If no exception is thrown, the flag remains false and the test fails:@Test public void fails_when_an_exception_is_thrown() { boolean isFailed = false; try { calc.divide(1, 0); }catch(Exception ex) { isFailed = true; } assertTrue(isFailed); }
Use the JUnit 4 patterns explained in the preceding example.
sum(int… arg)
method throws a NumberOverflowException
checked exception when the integer overflows:public int sum(int... args) throws NumberOverflowException{ int sum = 0; for(int val:args) { if(Integer.MAX_VALUE - sum < val) { throw new NumberOverflowException("Number overflow"); } sum+=val; } return sum; }
A catch block is used to catch a checked exception and compile the test, as follows:
@Test public void fails_when_an_exception_is_thrown() { try { int sum = calc.sum(1,2,3); assertEquals(6, sum); } catch (NumberOverflowException e) { } }
Do not follow this pattern; instead, use throws Exception
. The following JUnit test uses the throws Exception
clause:
@Test public void fails_when_an_exception_is_thrown() throws Exception { int sum = calc.sum(1,2,3); assertEquals(6, sum); }
Exceptions
from your tests. Instead, use the generic throws Exception
.The following example throws a specific NumberOverflowException
exception:
public void fails_when_an_exception_is_thrown() throws NumberOverflowException{
}
Suppose the code is changed such that it could throw either NumberOverflowException
or a ParseException
. In that case, we have to change the test method to throw both the exceptions to compile the test. If we use the generic throws Exception
clause, then this problem won't arise.