CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-truth--truth

Fluent assertion framework for Java that provides clear, readable test assertions and informative failure messages

Pending
Overview
Eval results
Files

exception-assertions.mddocs/

Exception Assertions

Specialized assertions for throwables and exceptions including message and cause chain validation.

Capabilities

Throwable Subject Creation

Entry point for creating assertions about exceptions and throwables.

/**
 * Creates a ThrowableSubject for asserting about Throwable instances.
 * @param actual the Throwable under test
 */
public static ThrowableSubject assertThat(Throwable actual);

Usage Examples:

try {
    someMethodThatThrows();
    fail("Expected exception to be thrown");
} catch (Exception e) {
    assertThat(e).isInstanceOf(IllegalArgumentException.class);
}

Exception Message Assertions

Methods for asserting about exception messages using StringSubject capabilities.

/**
 * Returns a StringSubject for making assertions about the exception's message.
 * The message is obtained via getMessage().
 */
public StringSubject hasMessageThat();

Usage Examples:

IllegalArgumentException exception = 
    new IllegalArgumentException("Invalid input: null value not allowed");

// Basic message assertions
assertThat(exception).hasMessageThat().contains("Invalid input");
assertThat(exception).hasMessageThat().startsWith("Invalid");
assertThat(exception).hasMessageThat().endsWith("not allowed");

// Pattern matching in messages
assertThat(exception).hasMessageThat().matches("Invalid input: .* not allowed");
assertThat(exception).hasMessageThat().containsMatch("null value");

// Case-insensitive message checks
assertThat(exception).hasMessageThat().ignoringCase().contains("INVALID");

Exception Cause Assertions

Methods for asserting about exception causes, enabling deep cause chain validation.

/**
 * Returns a ThrowableSubject for making assertions about the exception's cause.
 * The cause is obtained via getCause().
 */
public ThrowableSubject hasCauseThat();

Usage Examples:

// Exception with nested causes
IOException ioException = new IOException("File not found");
RuntimeException runtimeException = new RuntimeException("Processing failed", ioException);

// Asserting about direct cause
assertThat(runtimeException).hasCauseThat().isInstanceOf(IOException.class);
assertThat(runtimeException).hasCauseThat().hasMessageThat().contains("File not found");

// Chaining cause assertions for deep cause chains
DatabaseException dbException = new DatabaseException("Connection failed");
ServiceException serviceException = new ServiceException("Service unavailable", dbException);
ApplicationException appException = new ApplicationException("Request failed", serviceException);

assertThat(appException)
    .hasCauseThat().isInstanceOf(ServiceException.class)
    .hasCauseThat().isInstanceOf(DatabaseException.class)
    .hasMessageThat().contains("Connection failed");

Common Exception Testing Patterns

Real-world patterns for testing exception behavior in applications.

Testing Expected Exceptions

// Method that should throw exception
@Test
public void testInvalidInput_ThrowsException() {
    IllegalArgumentException exception = assertThrows(
        IllegalArgumentException.class, 
        () -> calculator.divide(10, 0)
    );
    
    assertThat(exception).hasMessageThat().contains("Division by zero");
}

// Using Truth with assertThrows
@Test 
public void testFileOperation_ThrowsIOException() {
    IOException exception = assertThrows(
        IOException.class,
        () -> fileService.readNonExistentFile("missing.txt")
    );
    
    assertThat(exception).hasMessageThat().startsWith("File not found");
    assertThat(exception).hasCauseThat().isNull();
}

Validation Exception Testing

// Testing validation exceptions with detailed messages
@Test
public void testUserValidation_InvalidEmail() {
    ValidationException exception = assertThrows(
        ValidationException.class,
        () -> userService.createUser("invalid-email", "password")
    );
    
    assertThat(exception).hasMessageThat().contains("email");
    assertThat(exception).hasMessageThat().matches(".*email.*format.*");
    assertThat(exception).hasMessageThat().ignoringCase().contains("INVALID");
}

// Testing multiple validation errors
@Test
public void testUserValidation_MultipleErrors() {
    ValidationException exception = assertThrows(
        ValidationException.class,
        () -> userService.createUser("", "")
    );
    
    assertThat(exception).hasMessageThat().contains("email");
    assertThat(exception).hasMessageThat().contains("password");
}

Exception Chaining Validation

// Testing proper exception wrapping in service layers
@Test
public void testServiceLayer_WrapsDataAccessExceptions() {
    // Simulate data access exception
    when(userRepository.findById(1L)).thenThrow(
        new DataAccessException("Database connection timeout")
    );
    
    ServiceException exception = assertThrows(
        ServiceException.class,
        () -> userService.getUser(1L)
    );
    
    // Verify exception wrapping
    assertThat(exception).hasMessageThat().contains("Failed to retrieve user");
    assertThat(exception).hasCauseThat().isInstanceOf(DataAccessException.class);
    assertThat(exception).hasCauseThat().hasMessageThat().contains("Database connection timeout");
}

Security Exception Testing

// Testing authentication exceptions
@Test
public void testAuthentication_InvalidCredentials() {
    AuthenticationException exception = assertThrows(
        AuthenticationException.class,
        () -> authService.authenticate("user", "wrongpassword")
    );
    
    assertThat(exception).hasMessageThat().contains("Invalid credentials");
    assertThat(exception).hasMessageThat().doesNotContain("password");  // Security: don't leak password details
}

// Testing authorization exceptions
@Test
public void testAuthorization_InsufficientPermissions() {
    AuthorizationException exception = assertThrows(
        AuthorizationException.class,
        () -> adminService.deleteUser(regularUser, targetUserId)
    );
    
    assertThat(exception).hasMessageThat().contains("Insufficient permissions");
    assertThat(exception).hasMessageThat().containsMatch("user.*not authorized");
}

Configuration Exception Testing

// Testing configuration validation
@Test
public void testConfiguration_MissingProperty() {
    ConfigurationException exception = assertThrows(
        ConfigurationException.class,
        () -> configService.loadConfig("invalid-config.properties")
    );
    
    assertThat(exception).hasMessageThat().contains("Missing required property");
    assertThat(exception).hasMessageThat().matches(".*database\\.url.*required.*");
}

// Testing configuration format errors
@Test
public void testConfiguration_InvalidFormat() {
    ConfigurationException exception = assertThrows(
        ConfigurationException.class,
        () -> configService.loadConfig("malformed-config.xml")
    );
    
    assertThat(exception).hasMessageThat().contains("Invalid XML format");
    assertThat(exception).hasCauseThat().isInstanceOf(XMLParseException.class);
}

Timeout and Resource Exception Testing

// Testing timeout exceptions
@Test
public void testNetworkCall_Timeout() {
    TimeoutException exception = assertThrows(
        TimeoutException.class,
        () -> networkService.callExternalApi()
    );
    
    assertThat(exception).hasMessageThat().contains("timeout");
    assertThat(exception).hasMessageThat().containsMatch("\\d+ms");  // Contains timeout duration
}

// Testing resource exhaustion
@Test
public void testResourceExhaustion_OutOfMemory() {
    ResourceException exception = assertThrows(
        ResourceException.class,
        () -> memoryIntensiveOperation()
    );
    
    assertThat(exception).hasMessageThat().contains("Out of memory");
    assertThat(exception).hasCauseThat().isInstanceOf(OutOfMemoryError.class);
}

Custom Exception Testing

// Testing custom business exceptions
@Test
public void testBusinessLogic_InvalidBusinessRule() {
    BusinessRuleException exception = assertThrows(
        BusinessRuleException.class,
        () -> orderService.processOrder(invalidOrder)
    );
    
    assertThat(exception).hasMessageThat().contains("Business rule violation");
    assertThat(exception).hasMessageThat().contains(invalidOrder.getId().toString());
    assertThat(exception.getErrorCode()).isEqualTo("INVALID_ORDER_STATE");
}

// Testing exception with custom properties
public class CustomException extends Exception {
    private final String errorCode;
    private final int statusCode;
    
    // constructor and getters...
}

@Test
public void testCustomException_Properties() {
    CustomException exception = assertThrows(
        CustomException.class,
        () -> customService.performOperation()
    );
    
    assertThat(exception).hasMessageThat().contains("Operation failed");
    assertThat(exception.getErrorCode()).isEqualTo("CUSTOM_ERROR");
    assertThat(exception.getStatusCode()).isEqualTo(500);
}

Exception Stack Trace Validation

// Testing that exception originates from expected location
@Test
public void testException_OriginatesFromCorrectLocation() {
    RuntimeException exception = assertThrows(
        RuntimeException.class,
        () -> serviceMethodThatShouldFail()
    );
    
    assertThat(exception).hasMessageThat().isNotNull();
    
    // Verify stack trace contains expected method
    StackTraceElement[] stackTrace = exception.getStackTrace();
    assertThat(stackTrace).isNotEmpty();
    
    boolean foundExpectedMethod = Arrays.stream(stackTrace)
        .anyMatch(element -> element.getMethodName().equals("serviceMethodThatShouldFail"));
    assertThat(foundExpectedMethod).isTrue();
}

Exception Testing Best Practices

Comprehensive Exception Assertion Pattern

/**
 * Comprehensive exception testing helper method
 */
public static void assertExceptionDetails(
    Throwable exception,
    Class<? extends Throwable> expectedType,
    String expectedMessageSubstring,
    Class<? extends Throwable> expectedCauseType) {
    
    assertThat(exception).isInstanceOf(expectedType);
    assertThat(exception).hasMessageThat().contains(expectedMessageSubstring);
    
    if (expectedCauseType != null) {
        assertThat(exception).hasCauseThat().isInstanceOf(expectedCauseType);
    }
}

// Usage
@Test
public void testComplexOperation_ProperExceptionHandling() {
    ServiceException exception = assertThrows(
        ServiceException.class,
        () -> complexService.performComplexOperation()
    );
    
    assertExceptionDetails(
        exception,
        ServiceException.class,
        "Complex operation failed",
        DataAccessException.class
    );
}

Types

/**
 * Subject class for making assertions about Throwable instances.
 */
public class ThrowableSubject extends Subject {
    /**
     * Constructor for ThrowableSubject.
     * @param metadata failure metadata for context
     * @param actual the Throwable under test
     */
    protected ThrowableSubject(FailureMetadata metadata, Throwable actual);
    
    /**
     * Returns a StringSubject for making assertions about the exception's message.
     * The message is obtained via getMessage().
     */
    public StringSubject hasMessageThat();
    
    /**
     * Returns a ThrowableSubject for making assertions about the exception's cause.
     * The cause is obtained via getCause().
     */
    public ThrowableSubject hasCauseThat();
}

Install with Tessl CLI

npx tessl i tessl/maven-com-google-truth--truth

docs

array-assertions.md

collection-assertions.md

core-assertions.md

custom-assertions.md

exception-assertions.md

index.md

java8-assertions.md

map-assertions.md

numeric-assertions.md

string-assertions.md

testing-utilities.md

tile.json