Common test assertions and utilities for Kotlin multiplatform projects
—
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.
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): ThrowableUsage 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}")
}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
): TUsage 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
}
}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
): TUsage 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)
}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")
}Exception testing functions will throw an AssertionError when conditions fail:
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")
}Always validate exception messages to ensure meaningful error reporting:
val exception = assertFailsWith<ValidationException> {
validateEmail("invalid-email")
}
assertContains(exception.message ?: "", "email")
assertContains(exception.message ?: "", "format")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)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