CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlin--kotlin-test-common

Common test assertions and utilities for Kotlin multiplatform projects

Pending
Overview
Eval results
Files

exception-testing.mddocs/

Exception Testing

Exception assertion functions for testing error conditions with support for specific exception types and cause chains. These functions are essential for validating that code properly handles error cases and throws appropriate exceptions.

Capabilities

assertFails

Asserts that a code block throws any exception and returns the thrown exception.

/**
 * Asserts that the given block fails by throwing an exception.
 * @param block The code block that should throw an exception
 * @return The thrown exception
 * @throws AssertionError if the block completes without throwing
 */
fun assertFails(block: () -> Unit): Throwable

/**
 * Asserts that the given block fails by throwing an exception.
 * @param message Optional message to show if assertion fails
 * @param block The code block that should throw an exception
 * @return The thrown exception
 * @throws AssertionError if the block completes without throwing
 * @since Kotlin 1.1
 */
fun assertFails(message: String?, block: () -> Unit): Throwable

Usage Examples:

import kotlin.test.assertFails
import kotlin.test.assertEquals

@Test
fun testGeneralExceptionHandling() {
    // Basic exception testing - any exception type
    val exception = assertFails("Should throw when dividing by zero") {
        val result = 10 / 0
    }
    assertTrue(exception is ArithmeticException)
    
    // Testing API error responses
    val apiException = assertFails {
        apiClient.fetchUser("invalid-id")
    }
    assertContains(apiException.message ?: "", "invalid")
    
    // Testing validation logic
    assertFails("Should fail with invalid email") {
        User.create(name = "John", email = "invalid-email")
    }
    
    // Testing concurrent operations
    val concurrencyException = assertFails {
        unsafeMap.putAll(generateLargeMap())
    }
    // Can examine the exception further
    println("Caught exception: ${concurrencyException.javaClass.simpleName}")
}

assertFailsWith (Reified)

Asserts that a code block throws a specific exception type using reified generics.

/**
 * Asserts that the given block fails with a specific exception type.
 * Uses reified generics for type-safe exception testing.
 * @param message Optional message to show if assertion fails
 * @param block The code block that should throw the exception
 * @return The thrown exception cast to the specified type
 * @throws AssertionError if block doesn't throw or throws wrong type
 */
inline fun <reified T : Throwable> assertFailsWith(
    message: String? = null, 
    block: () -> Unit
): T

Usage Examples:

import kotlin.test.assertFailsWith
import kotlin.test.assertEquals

@Test
fun testSpecificExceptionTypes() {
    // Test specific exception type with smart cast
    val illegalArg = assertFailsWith<IllegalArgumentException> {
        validateAge(-5)
    }
    assertEquals("Age cannot be negative", illegalArg.message)
    
    // Test custom exceptions
    val customException = assertFailsWith<UserNotFoundException>(
        "Should throw UserNotFoundException for missing user"
    ) {
        userService.getUser("nonexistent-id")
    }
    assertEquals("nonexistent-id", customException.userId)
    
    // Test standard library exceptions
    val indexException = assertFailsWith<IndexOutOfBoundsException> {
        val list = listOf(1, 2, 3)
        list[10] // Should throw
    }
    assertContains(indexException.message ?: "", "10")
    
    // Test null pointer exceptions
    val nullException = assertFailsWith<KotlinNullPointerException> {
        val nullString: String? = null
        nullString!!.length
    }
    
    // Test type cast exceptions
    val castException = assertFailsWith<ClassCastException> {
        val any: Any = "string"
        any as Int
    }
}

assertFailsWith (KClass)

Asserts that a code block throws a specific exception type using KClass reflection.

/**
 * Asserts that the given block fails with a specific exception class.
 * Uses KClass for runtime type checking.
 * @param exceptionClass The class of exception expected
 * @param block The code block that should throw the exception
 * @return The thrown exception cast to the specified type
 * @throws AssertionError if block doesn't throw or throws wrong type
 */
fun <T : Throwable> assertFailsWith(
    exceptionClass: KClass<T>, 
    block: () -> Unit
): T

/**
 * Asserts that the given block fails with a specific exception class.
 * @param exceptionClass The class of exception expected
 * @param message Optional message to show if assertion fails
 * @param block The code block that should throw the exception
 * @return The thrown exception cast to the specified type
 */
fun <T : Throwable> assertFailsWith(
    exceptionClass: KClass<T>, 
    message: String?, 
    block: () -> Unit
): T

Usage Examples:

import kotlin.test.assertFailsWith
import kotlin.reflect.KClass

@Test
fun testExceptionClassChecking() {
    // Using KClass directly
    val exception = assertFailsWith(IllegalStateException::class) {
        stateMachine.performInvalidTransition()
    }
    assertContains(exception.message ?: "", "invalid transition")
    
    // With custom message
    val networkException = assertFailsWith(
        NetworkException::class,
        "Should throw NetworkException for connection timeout"
    ) {
        networkClient.connectWithTimeout(timeout = 0)
    }
    assertEquals("Connection timeout", networkException.reason)
    
    // Dynamic exception type testing
    fun testDynamicException(expectedType: KClass<out Exception>) {
        assertFailsWith(expectedType) {
            riskyOperation()
        }
    }
    
    testDynamicException(SecurityException::class)
    testDynamicException(IllegalAccessException::class)
}

Exception Hierarchy Testing

Test exception inheritance and exception cause chains.

Usage Examples:

import kotlin.test.assertFailsWith
import kotlin.test.assertIs

@Test
fun testExceptionHierarchy() {
    // Test exception inheritance
    val runtimeException = assertFailsWith<RuntimeException> {
        // This throws IllegalArgumentException, which extends RuntimeException
        validateInput("")
    }
    // Can check specific subtype
    assertIs<IllegalArgumentException>(runtimeException)
    
    // Test exception causes
    val wrapperException = assertFailsWith<ServiceException> {
        serviceLayer.performComplexOperation()
    }
    
    // Check the cause chain
    val cause = wrapperException.cause
    assertNotNull(cause, "Exception should have a cause")
    assertIs<DatabaseException>(cause)
    
    val rootCause = cause.cause
    assertNotNull(rootCause, "Should have root cause")
    assertIs<SQLException>(rootCause)
    assertEquals("Connection timeout", rootCause.message)
}

@Test
fun testCustomExceptionProperties() {
    // Test custom exception with additional properties
    val validationException = assertFailsWith<ValidationException> {
        validator.validate(invalidData)
    }
    
    assertEquals("field1", validationException.fieldName)
    assertEquals("required", validationException.validationType)
    assertTrue(validationException.errors.isNotEmpty())
    
    // Test exception with error codes
    val apiException = assertFailsWith<ApiException> {
        apiClient.makeRequest("/invalid-endpoint")
    }
    
    assertEquals(404, apiException.statusCode)
    assertEquals("NOT_FOUND", apiException.errorCode)
    assertContains(apiException.details, "endpoint")
}

Error Handling

Exception testing functions will throw an AssertionError when conditions fail:

  • assertFails: Throws when the block completes without throwing any exception
  • assertFailsWith: Throws when the block doesn't throw or throws the wrong exception type

Error messages provide clear information about the mismatch:

// This will throw: AssertionError: Expected an exception to be thrown, but was completed successfully.
assertFails { 
    val result = 2 + 2 // No exception thrown
}

// This will throw: AssertionError: Expected an exception of type IllegalArgumentException, actual was RuntimeException
assertFailsWith<IllegalArgumentException> {
    throw RuntimeException("Wrong type")
}

Best Practices

Message Validation

Always validate exception messages to ensure meaningful error reporting:

val exception = assertFailsWith<ValidationException> {
    validateEmail("invalid-email")
}
assertContains(exception.message ?: "", "email")
assertContains(exception.message ?: "", "format")

Exception State Testing

Test that exceptions contain proper state information:

val businessException = assertFailsWith<InsufficientFundsException> {
    account.withdraw(1000.0)
}
assertEquals(account.balance, businessException.availableBalance)
assertEquals(1000.0, businessException.requestedAmount)

Cause Chain Validation

When testing wrapped exceptions, validate the entire cause chain:

val serviceException = assertFailsWith<ServiceException> {
    userService.createUser(invalidUserData)
}

// Check immediate cause
val validationCause = assertIs<ValidationException>(serviceException.cause)
assertEquals("Invalid user data", validationCause.message)

// Check root cause
val fieldCause = assertIs<FieldValidationException>(validationCause.cause)
assertEquals("email", fieldCause.fieldName)

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-test-common

docs

boolean-assertions.md

collection-assertions.md

equality-assertions.md

exception-testing.md

framework-integration.md

index.md

test-annotations.md

test-utilities.md

type-null-assertions.md

tile.json