Handling exceptions

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:

  • Do not write catch blocks to pass a unit test. Consider the following example where a 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.

  • Do not write catch blocks to fail a test; the JUnit framework takes care of runtime exceptions. The following is an example of an unnecessary catch block:
      @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);
      }
  • Do not catch an exception and assert the failure to pass a test. The following test code catches 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.

  • Do not add catch blocks to test a method that throws checked exceptions. The following example explains the problem. The 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);
      }
  • Do not throw specific 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset