CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-kotest--kotest-assertions-core-jvm

Core assertion and matcher library for Kotest testing framework

Pending
Overview
Eval results
Files

nondeterministic.mddocs/

Nondeterministic Testing

Support for testing asynchronous and eventually consistent systems with configurable retry strategies and timing controls.

Capabilities

Eventually Functions

Test assertions that may initially fail but should eventually succeed within a time limit.

/**
 * Repeatedly execute test until it passes or timeout is reached
 * Uses default configuration (5 seconds duration, 25ms interval)
 * @param test The test code to execute repeatedly
 * @return The result of the successful test execution
 */
suspend fun <T> eventually(test: suspend () -> T): T

/**
 * Repeatedly execute test until it passes or specified duration elapses
 * @param duration Maximum time to keep retrying
 * @param test The test code to execute repeatedly
 * @return The result of the successful test execution
 */
suspend fun <T> eventually(duration: Duration, test: suspend () -> T): T

/**
 * Repeatedly execute test with custom configuration
 * @param config Configuration for retry behavior
 * @param test The test code to execute repeatedly
 * @return The result of the successful test execution
 */
suspend fun <T> eventually(config: EventuallyConfiguration, test: suspend () -> T): T

Usage Examples:

import io.kotest.assertions.nondeterministic.eventually
import io.kotest.matchers.shouldBe
import kotlin.time.Duration.Companion.seconds

// Basic eventually with defaults (5 second timeout)
eventually {
    remoteService.isHealthy() shouldBe true
}

// Custom timeout
eventually(10.seconds) {
    database.countRecords() shouldBe 100
}

// With custom configuration
val config = eventuallyConfig {
    duration = 30.seconds
    interval = 100.milliseconds
    initialDelay = 1.seconds
}

eventually(config) {
    queue.isEmpty() shouldBe true
}

Continually Functions

Test assertions that should remain true for a specified duration.

/**
 * Repeatedly execute test and verify it continues to pass
 * @param test The test code to execute repeatedly
 * @return The result of the test execution
 */
suspend fun <T> continually(test: suspend () -> T): T

/**
 * Repeatedly execute test for specified duration
 * @param duration How long to keep testing
 * @param test The test code to execute repeatedly
 * @return The result of the test execution
 */
suspend fun <T> continually(duration: Duration, test: suspend () -> T): T

/**
 * Repeatedly execute test with custom configuration
 * @param config Configuration for continual testing
 * @param test The test code to execute repeatedly
 * @return The result of the test execution
 */
suspend fun <T> continually(config: ContinuallyConfiguration, test: suspend () -> T): T

Usage Examples:

import io.kotest.assertions.nondeterministic.continually
import io.kotest.matchers.shouldBe

// Verify service stays healthy for 30 seconds
continually(30.seconds) {
    healthCheck.status shouldBe "OK"
}

// Verify memory usage stays under limit
continually {
    Runtime.getRuntime().freeMemory() should beGreaterThan(1000000)
}

Until Functions

Execute test repeatedly until it returns without throwing an exception.

/**
 * Execute test repeatedly until it succeeds (doesn't throw)
 * @param test The test code to execute repeatedly
 * @return The result of the successful test execution
 */
suspend fun <T> until(test: suspend () -> T): T

/**
 * Execute test repeatedly until it succeeds or timeout
 * @param duration Maximum time to keep trying
 * @param test The test code to execute repeatedly  
 * @return The result of the successful test execution
 */
suspend fun <T> until(duration: Duration, test: suspend () -> T): T

/**
 * Execute test repeatedly with custom configuration
 * @param config Configuration for retry behavior
 * @param test The test code to execute repeatedly
 * @return The result of the successful test execution
 */
suspend fun <T> until(config: UntilConfiguration, test: suspend () -> T): T

Configuration Classes

Configuration objects for customizing retry behavior and intervals.

/**
 * Configuration for eventually testing
 */
data class EventuallyConfiguration(
    val duration: Duration = 5.seconds,
    val interval: Duration = 25.milliseconds, 
    val initialDelay: Duration = Duration.ZERO,
    val listener: EventuallyListener = NoopEventuallyListener
)

/**
 * Builder for creating EventuallyConfiguration
 */
class EventuallyConfigurationBuilder {
    var duration: Duration = 5.seconds
    var interval: Duration = 25.milliseconds
    var initialDelay: Duration = Duration.ZERO
    var listener: EventuallyListener = NoopEventuallyListener
}

/**
 * Configuration for continually testing
 */
data class ContinuallyConfiguration<T>(
    val duration: Duration = 1.seconds,
    val interval: Duration = 25.milliseconds,
    val listener: ContinuallyListener<T> = NoopContinuallyListener
)

/**
 * Configuration for until testing
 */
data class UntilConfiguration(
    val duration: Duration = 5.seconds,
    val interval: Duration = 25.milliseconds,
    val listener: UntilListener = NoopUntilListener
)

Configuration Usage:

import io.kotest.assertions.nondeterministic.*

// Create configuration using builder
val config = eventuallyConfig {
    duration = 60.seconds      // Total timeout
    interval = 500.milliseconds // Time between attempts
    initialDelay = 2.seconds   // Wait before first attempt
    listener = { attempt, error ->
        println("Attempt $attempt failed: ${error.message}")
    }
}

eventually(config) {
    externalApi.getData() shouldBe expectedData
}

Event Listeners

Monitor retry attempts and failures during nondeterministic testing.

/**
 * Listener for eventually test attempts and failures
 */
typealias EventuallyListener = suspend (Int, Throwable) -> Unit

/**
 * Listener for continually test executions
 */
typealias ContinuallyListener<T> = suspend (Int, T) -> Unit

/**
 * Listener for until test attempts and failures
 */
typealias UntilListener = suspend (Int, Throwable) -> Unit

/**
 * No-op implementation that ignores all events
 */
object NoopEventuallyListener : EventuallyListener {
    override suspend fun invoke(attempt: Int, error: Throwable) {}
}

Interval Functions

Advanced retry strategies with customizable backoff patterns.

/**
 * Interface for generating intervals between retry attempts
 */
interface DurationFn {
    fun next(iteration: Int): Duration
}

/**
 * Fibonacci backoff strategy with maximum duration limit
 */
class FibonacciIntervalFn(private val max: Duration) : DurationFn {
    override fun next(iteration: Int): Duration
}

/**
 * Exponential backoff strategy  
 */
class ExponentialIntervalFn(
    private val base: Duration = 25.milliseconds,
    private val factor: Double = 2.0,
    private val max: Duration = 1.seconds
) : DurationFn {
    override fun next(iteration: Int): Duration
}

/**
 * Create a fibonacci backoff function with maximum duration
 * @receiver Base duration for calculations
 * @param max Maximum duration to cap the backoff
 * @return FibonacciIntervalFn instance
 */
fun Duration.fibonacci(max: Duration): FibonacciIntervalFn

Advanced Usage Examples:

import io.kotest.assertions.nondeterministic.*

// Using fibonacci backoff
val fibConfig = eventuallyConfig {
    duration = 2.minutes
    interval = FibonacciIntervalFn(10.seconds)
}

// Using exponential backoff
val expConfig = eventuallyConfig {
    duration = 1.minutes
    interval = ExponentialIntervalFn(
        base = 100.milliseconds,
        factor = 1.5,
        max = 5.seconds
    )
}

// Monitor retry attempts
val monitoredConfig = eventuallyConfig {
    duration = 30.seconds
    listener = { attempt, error ->
        logger.warn("Retry attempt $attempt failed", error)
        if (attempt > 10) {
            metrics.increment("high_retry_count")
        }
    }
}

Error Handling

Nondeterministic testing functions provide detailed error information:

  • Timeout errors: Include total duration, attempt count, and last failure
  • Listener errors: Exceptions in listeners don't affect test execution
  • Configuration validation: Invalid durations or intervals cause immediate failure
  • Thread safety: All functions are thread-safe and work with coroutines

All functions throw AssertionError when the final attempt fails, preserving the original assertion failure message.

Install with Tessl CLI

npx tessl i tessl/maven-io-kotest--kotest-assertions-core-jvm

docs

collections.md

concurrency.md

core-dsl.md

datetime.md

filesystem.md

index.md

nondeterministic.md

primitives.md

reflection.md

result.md

strings.md

throwable.md

tuples.md

types.md

tile.json