Core assertion and matcher library for Kotest testing framework
—
JVM concurrency testing support for futures, atomic types, channels, thread-safe collections, and synchronization primitives with comprehensive assertions for concurrent operations and thread safety validation.
Matchers for validating CompletableFuture behavior including completion, timing, and result validation.
/**
* Assert that CompletableFuture completes within specified duration
* @param duration Maximum time to wait for completion
* @return The original CompletableFuture for chaining
*/
fun <T> CompletableFuture<T>.shouldCompleteWithin(duration: Duration): CompletableFuture<T>
/**
* Assert that CompletableFuture does not complete within specified duration
* @param duration Time to wait before asserting non-completion
* @return The original CompletableFuture for chaining
*/
fun <T> CompletableFuture<T>.shouldNotCompleteWithin(duration: Duration): CompletableFuture<T>
/**
* Assert that CompletableFuture completes successfully (not exceptionally)
* @return The original CompletableFuture for chaining
*/
fun <T> CompletableFuture<T>.shouldCompleteSuccessfully(): CompletableFuture<T>
/**
* Assert that CompletableFuture completes exceptionally
* @return The original CompletableFuture for chaining
*/
fun <T> CompletableFuture<T>.shouldCompleteExceptionally(): CompletableFuture<T>
/**
* Assert that CompletableFuture completes with specific value
* @param expectedValue The value the future should complete with
* @return The original CompletableFuture for chaining
*/
infix fun <T> CompletableFuture<T>.shouldCompleteWith(expectedValue: T): CompletableFuture<T>
/**
* Assert that CompletableFuture completes with exception of specific type
* @param T The exception type expected
* @return The original CompletableFuture for chaining
*/
inline fun <reified T : Throwable> CompletableFuture<*>.shouldCompleteWithException(): CompletableFuture<*>
/**
* Assert that CompletableFuture is already done (completed or failed)
* @return The original CompletableFuture for chaining
*/
fun <T> CompletableFuture<T>.shouldBeDone(): CompletableFuture<T>
/**
* Assert that CompletableFuture is not yet done
* @return The original CompletableFuture for chaining
*/
fun <T> CompletableFuture<T>.shouldNotBeDone(): CompletableFuture<T>
/**
* Assert that CompletableFuture is cancelled
* @return The original CompletableFuture for chaining
*/
fun <T> CompletableFuture<T>.shouldBeCancelled(): CompletableFuture<T>
/**
* Create matcher for future completion within duration
* @param duration Maximum time to wait for completion
* @return Matcher that passes when future completes within time
*/
fun <T> completeWithin(duration: Duration): Matcher<CompletableFuture<T>>
/**
* Create matcher for successful completion
* @return Matcher that passes when future completes successfully
*/
fun <T> completeSuccessfully(): Matcher<CompletableFuture<T>>
/**
* Create matcher for exceptional completion
* @return Matcher that passes when future completes exceptionally
*/
fun <T> completeExceptionally(): Matcher<CompletableFuture<T>>
/**
* Create matcher for completion with specific value
* @param expectedValue The expected completion value
* @return Matcher that passes when future completes with expected value
*/
fun <T> completeWith(expectedValue: T): Matcher<CompletableFuture<T>>
/**
* Create matcher for completion with exception type
* @param T The expected exception type
* @return Matcher that passes when future completes with specified exception
*/
inline fun <reified T : Throwable> completeWithException(): Matcher<CompletableFuture<*>>Usage Examples:
import io.kotest.matchers.concurrent.*
import java.util.concurrent.CompletableFuture
import java.time.Duration
val quickTask = CompletableFuture.supplyAsync { "result" }
val slowTask = CompletableFuture.supplyAsync {
Thread.sleep(1000)
"slow result"
}
val failingTask = CompletableFuture.supplyAsync<String> {
throw RuntimeException("Task failed")
}
// Completion timing
quickTask.shouldCompleteWithin(Duration.ofSeconds(1))
slowTask.shouldNotCompleteWithin(Duration.ofMillis(100))
// Completion outcomes
quickTask.shouldCompleteSuccessfully()
failingTask.shouldCompleteExceptionally()
quickTask shouldCompleteWith "result"
failingTask.shouldCompleteWithException<RuntimeException>()
// State validation
quickTask.shouldBeDone() // after completion
val pendingTask = CompletableFuture<String>()
pendingTask.shouldNotBeDone()
// Using matcher syntax
quickTask should completeSuccessfully()
failingTask should completeExceptionally()
slowTask should completeWithin(Duration.ofSeconds(2))Matchers for validating atomic reference types and their thread-safe operations.
/**
* Assert that AtomicReference has specific value
* @param expected The expected value
* @return The original AtomicReference for chaining
*/
fun <T> AtomicReference<T>.shouldHaveValue(expected: T): AtomicReference<T>
/**
* Assert that AtomicReference does not have specific value
* @param unexpected The value that should not be present
* @return The original AtomicReference for chaining
*/
fun <T> AtomicReference<T>.shouldNotHaveValue(unexpected: T): AtomicReference<T>
/**
* Assert that AtomicInteger has specific value
* @param expected The expected integer value
* @return The original AtomicInteger for chaining
*/
infix fun AtomicInteger.shouldHaveValue(expected: Int): AtomicInteger
/**
* Assert that AtomicLong has specific value
* @param expected The expected long value
* @return The original AtomicLong for chaining
*/
infix fun AtomicLong.shouldHaveValue(expected: Long): AtomicLong
/**
* Assert that AtomicBoolean has specific value
* @param expected The expected boolean value
* @return The original AtomicBoolean for chaining
*/
infix fun AtomicBoolean.shouldHaveValue(expected: Boolean): AtomicBoolean
/**
* Assert that AtomicInteger is greater than specified value
* @param value The minimum value (exclusive)
* @return The original AtomicInteger for chaining
*/
infix fun AtomicInteger.shouldBeGreaterThan(value: Int): AtomicInteger
/**
* Assert that AtomicInteger is less than specified value
* @param value The maximum value (exclusive)
* @return The original AtomicInteger for chaining
*/
infix fun AtomicInteger.shouldBeLessThan(value: Int): AtomicInteger
/**
* Create matcher for atomic reference value validation
* @param expected The expected value
* @return Matcher that passes when atomic reference has expected value
*/
fun <T> haveValue(expected: T): Matcher<AtomicReference<T>>
/**
* Create matcher for atomic integer comparison (greater than)
* @param value The minimum value (exclusive)
* @return Matcher that passes when atomic integer is greater
*/
fun beGreaterThan(value: Int): Matcher<AtomicInteger>
/**
* Create matcher for atomic boolean validation
* @param expected The expected boolean value
* @return Matcher that passes when atomic boolean has expected value
*/
fun haveValue(expected: Boolean): Matcher<AtomicBoolean>Matchers for Kotlin coroutines channels including send/receive operations and channel states.
/**
* Assert that channel receives element within specified duration
* @param duration Maximum time to wait for receive
* @return The received element
*/
suspend fun <T> Channel<T>.shouldReceiveWithin(duration: Duration): T
/**
* Assert that channel does not receive element within specified duration
* @param duration Time to wait before asserting no receive
* @return The original Channel for chaining
*/
suspend fun <T> Channel<T>.shouldNotReceiveWithin(duration: Duration): Channel<T>
/**
* Assert that channel receives specific element within duration
* @param expected The expected element
* @param duration Maximum time to wait for receive
* @return The original Channel for chaining
*/
suspend fun <T> Channel<T>.shouldReceive(expected: T, duration: Duration): Channel<T>
/**
* Assert that channel is closed
* @return The original Channel for chaining
*/
fun <T> Channel<T>.shouldBeClosed(): Channel<T>
/**
* Assert that channel is not closed
* @return The original Channel for chaining
*/
fun <T> Channel<T>.shouldNotBeClosed(): Channel<T>
/**
* Assert that channel is empty (no elements available)
* @return The original Channel for chaining
*/
fun <T> Channel<T>.shouldBeEmpty(): Channel<T>
/**
* Assert that channel is not empty (has elements available)
* @return The original Channel for chaining
*/
fun <T> Channel<T>.shouldNotBeEmpty(): Channel<T>
/**
* Assert that channel send operation completes within duration
* @param element The element to send
* @param duration Maximum time to wait for send completion
* @return The original Channel for chaining
*/
suspend fun <T> Channel<T>.shouldSendWithin(element: T, duration: Duration): Channel<T>
/**
* Create matcher for channel receive within duration
* @param duration Maximum time to wait
* @return Matcher that passes when channel receives within time
*/
fun <T> receiveWithin(duration: Duration): Matcher<Channel<T>>
/**
* Create matcher for closed channel validation
* @return Matcher that passes for closed channels
*/
fun <T> beClosed(): Matcher<Channel<T>>
/**
* Create matcher for empty channel validation
* @return Matcher that passes for empty channels
*/
fun <T> beEmpty(): Matcher<Channel<T>>Usage Examples:
import io.kotest.matchers.concurrent.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import java.util.concurrent.atomic.*
import java.time.Duration
// Atomic reference validation
val atomicRef = AtomicReference("initial")
val atomicInt = AtomicInteger(42)
val atomicBool = AtomicBoolean(true)
atomicRef.shouldHaveValue("initial")
atomicInt shouldHaveValue 42
atomicBool shouldHaveValue true
atomicInt shouldBeGreaterThan 40
// Channel operations
runBlocking {
val channel = Channel<String>(Channel.UNLIMITED)
// Send and receive operations
channel.send("message")
channel.shouldReceiveWithin(Duration.ofSeconds(1)) shouldBe "message"
// Channel state validation
channel.shouldNotBeClosed()
channel.close()
channel.shouldBeClosed()
// Empty channel validation
val emptyChannel = Channel<Int>()
emptyChannel.shouldBeEmpty()
}
// Using matcher syntax
atomicRef should haveValue("initial")
atomicInt should beGreaterThan(30)Matchers for validating thread states and executor service operations.
/**
* Assert that thread is alive (not terminated)
* @return The original Thread for chaining
*/
fun Thread.shouldBeAlive(): Thread
/**
* Assert that thread is not alive (terminated)
* @return The original Thread for chaining
*/
fun Thread.shouldNotBeAlive(): Thread
/**
* Assert that thread is in specific state
* @param state The expected thread state
* @return The original Thread for chaining
*/
infix fun Thread.shouldBeInState(state: Thread.State): Thread
/**
* Assert that thread should not be in specific state
* @param state The thread state that should not match
* @return The original Thread for chaining
*/
infix fun Thread.shouldNotBeInState(state: Thread.State): Thread
/**
* Assert that thread is daemon thread
* @return The original Thread for chaining
*/
fun Thread.shouldBeDaemon(): Thread
/**
* Assert that thread is not daemon thread
* @return The original Thread for chaining
*/
fun Thread.shouldNotBeDaemon(): Thread
/**
* Assert that ExecutorService is shutdown
* @return The original ExecutorService for chaining
*/
fun ExecutorService.shouldBeShutdown(): ExecutorService
/**
* Assert that ExecutorService is not shutdown
* @return The original ExecutorService for chaining
*/
fun ExecutorService.shouldNotBeShutdown(): ExecutorService
/**
* Assert that ExecutorService is terminated
* @return The original ExecutorService for chaining
*/
fun ExecutorService.shouldBeTerminated(): ExecutorService
/**
* Assert that ExecutorService terminates within specified duration
* @param duration Maximum time to wait for termination
* @return The original ExecutorService for chaining
*/
fun ExecutorService.shouldTerminateWithin(duration: Duration): ExecutorService
/**
* Create matcher for thread state validation
* @param state The expected thread state
* @return Matcher that passes when thread is in specified state
*/
fun beInState(state: Thread.State): Matcher<Thread>
/**
* Create matcher for alive thread validation
* @return Matcher that passes for alive threads
*/
fun beAlive(): Matcher<Thread>
/**
* Create matcher for daemon thread validation
* @return Matcher that passes for daemon threads
*/
fun beDaemon(): Matcher<Thread>
/**
* Create matcher for shutdown executor validation
* @return Matcher that passes for shutdown executors
*/
fun beShutdown(): Matcher<ExecutorService>
/**
* Create matcher for executor termination validation
* @param duration Maximum time to wait for termination
* @return Matcher that passes when executor terminates within duration
*/
fun terminateWithin(duration: Duration): Matcher<ExecutorService>Matchers for validating locks, semaphores, and other synchronization primitives.
/**
* Assert that ReentrantLock is locked
* @return The original ReentrantLock for chaining
*/
fun ReentrantLock.shouldBeLocked(): ReentrantLock
/**
* Assert that ReentrantLock is not locked (unlocked)
* @return The original ReentrantLock for chaining
*/
fun ReentrantLock.shouldNotBeLocked(): ReentrantLock
/**
* Assert that ReentrantLock is held by current thread
* @return The original ReentrantLock for chaining
*/
fun ReentrantLock.shouldBeHeldByCurrentThread(): ReentrantLock
/**
* Assert that ReentrantLock is not held by current thread
* @return The original ReentrantLock for chaining
*/
fun ReentrantLock.shouldNotBeHeldByCurrentThread(): ReentrantLock
/**
* Assert that ReentrantLock has specific hold count
* @param count Expected number of times lock is held by current thread
* @return The original ReentrantLock for chaining
*/
infix fun ReentrantLock.shouldHaveHoldCount(count: Int): ReentrantLock
/**
* Assert that Semaphore has specific number of available permits
* @param permits Expected number of available permits
* @return The original Semaphore for chaining
*/
infix fun Semaphore.shouldHaveAvailablePermits(permits: Int): Semaphore
/**
* Assert that Semaphore has no available permits
* @return The original Semaphore for chaining
*/
fun Semaphore.shouldHaveNoAvailablePermits(): Semaphore
/**
* Assert that CountDownLatch count is zero (released)
* @return The original CountDownLatch for chaining
*/
fun CountDownLatch.shouldBeReleased(): CountDownLatch
/**
* Assert that CountDownLatch count is not zero (not released)
* @return The original CountDownLatch for chaining
*/
fun CountDownLatch.shouldNotBeReleased(): CountDownLatch
/**
* Assert that CountDownLatch has specific count
* @param count Expected latch count
* @return The original CountDownLatch for chaining
*/
infix fun CountDownLatch.shouldHaveCount(count: Long): CountDownLatch
/**
* Create matcher for locked state validation
* @return Matcher that passes for locked locks
*/
fun beLocked(): Matcher<ReentrantLock>
/**
* Create matcher for current thread lock ownership
* @return Matcher that passes when current thread holds lock
*/
fun beHeldByCurrentThread(): Matcher<ReentrantLock>
/**
* Create matcher for lock hold count validation
* @param count Expected hold count
* @return Matcher that passes when lock has expected hold count
*/
fun haveHoldCount(count: Int): Matcher<ReentrantLock>
/**
* Create matcher for semaphore permits validation
* @param permits Expected available permits
* @return Matcher that passes when semaphore has expected permits
*/
fun haveAvailablePermits(permits: Int): Matcher<Semaphore>
/**
* Create matcher for latch release validation
* @return Matcher that passes for released latches
*/
fun beReleased(): Matcher<CountDownLatch>Usage Examples:
import io.kotest.matchers.concurrent.*
import java.util.concurrent.*
import java.util.concurrent.locks.ReentrantLock
import java.time.Duration
// Thread validation
val thread = Thread {
Thread.sleep(1000)
}
thread.start()
thread.shouldBeAlive()
thread shouldBeInState Thread.State.RUNNABLE
thread.shouldNotBeDaemon()
// Executor service validation
val executor = Executors.newFixedThreadPool(2)
executor.shouldNotBeShutdown()
executor.shutdown()
executor.shouldBeShutdown()
executor.shouldTerminateWithin(Duration.ofSeconds(5))
// Lock validation
val lock = ReentrantLock()
lock.shouldNotBeLocked()
lock.lock()
try {
lock.shouldBeLocked()
lock.shouldBeHeldByCurrentThread()
lock shouldHaveHoldCount 1
} finally {
lock.unlock()
}
// Semaphore validation
val semaphore = Semaphore(3)
semaphore shouldHaveAvailablePermits 3
semaphore.acquire()
semaphore shouldHaveAvailablePermits 2
// CountDownLatch validation
val latch = CountDownLatch(2)
latch.shouldNotBeReleased()
latch shouldHaveCount 2L
latch.countDown()
latch shouldHaveCount 1L
latch.countDown()
latch.shouldBeReleased()
// Using matcher syntax
thread should beAlive()
lock should beLocked()
semaphore should haveAvailablePermits(2)
latch should beReleased()Matchers for thread-safe collections and their concurrent properties.
/**
* Assert that ConcurrentMap contains key-value pair atomically
* @param key The expected key
* @param value The expected value
* @return The original ConcurrentMap for chaining
*/
fun <K, V> ConcurrentMap<K, V>.shouldContainAtomically(key: K, value: V): ConcurrentMap<K, V>
/**
* Assert that ConcurrentHashMap is empty atomically
* @return The original ConcurrentHashMap for chaining
*/
fun <K, V> ConcurrentHashMap<K, V>.shouldBeEmptyAtomically(): ConcurrentHashMap<K, V>
/**
* Assert that BlockingQueue contains specific element
* @param element The element that should be present
* @return The original BlockingQueue for chaining
*/
fun <T> BlockingQueue<T>.shouldContain(element: T): BlockingQueue<T>
/**
* Assert that BlockingQueue is empty
* @return The original BlockingQueue for chaining
*/
fun <T> BlockingQueue<T>.shouldBeEmpty(): BlockingQueue<T>
/**
* Assert that BlockingQueue has specific size
* @param size Expected queue size
* @return The original BlockingQueue for chaining
*/
infix fun <T> BlockingQueue<T>.shouldHaveSize(size: Int): BlockingQueue<T>
/**
* Assert that BlockingQueue offers element within duration
* @param element The element to offer
* @param duration Maximum time to wait for offer
* @return The original BlockingQueue for chaining
*/
suspend fun <T> BlockingQueue<T>.shouldOfferWithin(element: T, duration: Duration): BlockingQueue<T>
/**
* Create matcher for concurrent map containment
* @param key Expected key
* @param value Expected value
* @return Matcher that passes when map contains key-value pair
*/
fun <K, V> containAtomically(key: K, value: V): Matcher<ConcurrentMap<K, V>>
/**
* Create matcher for blocking queue element validation
* @param element Expected element
* @return Matcher that passes when queue contains element
*/
fun <T> contain(element: T): Matcher<BlockingQueue<T>>
/**
* Create matcher for queue size validation
* @param size Expected size
* @return Matcher that passes when queue has expected size
*/
fun <T> haveSize(size: Int): Matcher<BlockingQueue<T>>Usage Examples:
import io.kotest.matchers.concurrent.*
import java.util.concurrent.*
// Concurrent collections
val concurrentMap = ConcurrentHashMap<String, Int>()
val blockingQueue = LinkedBlockingQueue<String>()
// Map operations
concurrentMap["key"] = 42
concurrentMap.shouldContainAtomically("key", 42)
// Queue operations
blockingQueue.offer("item1")
blockingQueue.offer("item2")
blockingQueue.shouldContain("item1")
blockingQueue shouldHaveSize 2
blockingQueue.shouldNotBeEmpty()
// Clear and verify
blockingQueue.clear()
blockingQueue.shouldBeEmpty()
// Using matcher syntax
concurrentMap should containAtomically("key", 42)
blockingQueue should haveSize(0)Concurrency matchers provide detailed error information for assertion failures:
All concurrency matchers handle race conditions appropriately and provide thread-safe assertions with meaningful error messages that help debug concurrent code issues.
Install with Tessl CLI
npx tessl i tessl/maven-io-kotest--kotest-assertions-core-jvm