Verify the Exception Type

As we showed in Chapter 8, the object-oriented testing frameworks make it easy to verify that a particular type of exception is thrown. Oftentimes, this is sufficient to determine the correctness of our code. Many methods only throw a single exception or only have a single error handling code path that requires testing.

The code in Listing 11-1 typifies a unified exception handling strategy in which errors are wrapped in an application-specific exception tailored to the context.

Listing 11-1: A typical unified exception handling strategy

public class SomeClass {
  public void doSomething() throws ServiceFailureException {
    try {
      makeNetworkRequest();
    } catch (RemoteException rexp) {
      throw new ServiceFailureException (rexp);
    }
  }
}

All RemoteExceptions thrown by makeNetworkRequest() will be wrapped in an exception specific to the context: in this case, the trite act of “doing something.” You could convincingly argue that the value added by the error handling in this method is simply the wrapping and that the particular derived RemoteException is irrelevant to the operation of the method. In that case, verifying that a ServiceFailureException is thrown may be sufficient if makeNetworkRequest() cannot also throw that exception. The JUnit test for this code is in Listing 11-2.

Listing 11-2: JUnit4 test for Listing 11-1

@Test(expected = ServiceFailureException.class)
public void testDoSomething_Exception() {
  // Create fixture
  sut.doSomething();
}

The expected attribute of the @Test annotation tells JUnit to only consider the test as passed if an exception of the specified type is thrown. If another type of exception is thrown, or no exception is thrown, JUnit fails the test.

Using annotations greatly simplifies writing error condition tests. If you are using a language or framework without equivalent functionality, you may need to write the equivalent behavior yourself. Although the pattern is straightforward, some details are commonly missed when implementing it for the first time. Listing 11-3 demonstrates correct exception testing without direct framework support.

Listing 11-3: JUnit test for Listing 11-1 with hand-coded exception handling

public void testDoSomething_Exception() {
  // Create fixture
  try {
    sut.doSomething();
    fail();
  } catch(ServiceFailureException expected) {
    // Expected behavior
  }
}

We still rely on framework behavior to fail if any exception other than ServiceFailureException is thrown. If that functionality is not present, then use another catch clause with a fail() assertion.

The included catch clause deliberately ignores the exception we expect. The comment in the clause clearly communicates that this is the expected result; the comment will also suffice for many static checkers so they do not complain about an empty code block, something that I deem acceptable only in a testing context.

The key point that inexperienced testers often miss is the use of the fail() assertion in the try block. What would happen if the method accidentally succeeded without throwing an exception? Control would flow past it, out of the try block, past the catch block, and out of the method without encountering an assertion, resulting in a passed test. The additional assertion prevents a false positive result.

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

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