Verify the Exception Message

What should we do if our exception handling is not as simple and clean as in Listing 11-1? Perhaps we want to include a message with the exception that depends on the exact RemoteException thrown. Maybe we are writing an object-oriented wrapper around a procedural API and want to throw a different exception or include a different message depending on the error value. Listing 11-4 gives a simple example of the former scenario.

Listing 11-4: Exception handling with multiple isomorphic exception paths. An alternative implementation would create an exception hierarchy from ServiceFailureException to distinguish the cases using techniques from the previous section.

public class SomeClass {
  public static final String FAILED_TO_CONNECT =
      "Failed to connect";
  public static final String SERVER_FAILURE =
      "Server failure";

  public void doSomething() throws ServiceFailureException {
    try {
      makeNetworkRequest();
    } catch (ConnectException exp) {
      throw new ServiceFailureException(FAILED_TO_CONNECT,
          exp);
    } catch (ServerException exp) {
      throw new ServiceFailureException(SERVER_FAILURE, exp);
    }
  }
}

Both ConnectException and ServerException derive from RemoteException, as well as many others. Two suffice for illustration. How do we distinguish between the two variations of ServiceFailureException that the method can throw? Leveraging our prior discussion of string handling, we have prepared the code for the answer. The two things that distinguish the resulting exception are the message and the causal exception. For the moment, let’s focus on the message.

The JUnit framework only provides direct support for expected exception classes, so we need to use manual exception verification if we want to verify details: in this case, the message string. Listing 11-5 shows how to do this.

Listing 11-5: Verifying an exception message in JUnit

public void testDoSomething_ConnectException() {
  // Create fixture to inject ConnectException
  try {
    sut.doSomething();
    fail();
  } catch(ServiceFailureException exp) {
    assertEquals(SomeClass.FAILED_TO_CONNECT,
        exp.getMessage());
  }
}

This is such a common pattern that some frameworks like TestNG have more explicit support for message-based verification. Listing 11-6 shows how we would write this using TestNG.

Listing 11-6: Verifying an exception message in TestNG

@Test(expectedExceptions = ServiceFailureException.class,
  expectedExceptionsMessageRegExp =
      SomeClass.FAILED_TO_CONNECT)
public void testDoSomething_ConnectException() {
  // Create fixture to inject ConnectException
  sut.doSomething();
}

This makes for a much clearer test. Not only will TestNG require that a ServiceFailureException is thrown, but the test will only pass if the specified regular expression matches the message. In our case, the regular expression is only a string constant. Relying on the full regular expression capability increases the power of this feature. In addition to the limitations noted in the section Verification by Pattern in Chapter 7, Java imposes further limits on the kinds of expressions that can be used as annotation arguments. All annotation arguments must be constants that can be resolved at compile time.

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

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