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.
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.
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.
@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.
3.135.188.121