CtrlK
BlogDocsLog inGet started
Tessl Logo

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

Core assertion building blocks for Kotest testing framework providing foundational utilities like shouldBe for all platforms

Pending
Overview
Eval results
Files

data-driven-testing.mddocs/

Data-Driven Testing

The data-driven testing framework provides comprehensive table-based testing capabilities with full type safety. It supports up to 22-column tables with strongly-typed row definitions and automatic test execution across all data combinations.

Capabilities

Row Definitions

Type-safe row containers for organizing test data. Each row type corresponds to a specific number of columns.

/**
 * Base interface for all row types
 */
interface Row {
    fun values(): List<Any?>
}

/**
 * Single-column row
 */
data class Row1<out A>(val a: A) : Row

/**
 * Two-column row  
 */
data class Row2<out A, out B>(val a: A, val b: B) : Row

/**
 * Three-column row
 */
data class Row3<out A, out B, out C>(val a: A, val b: B, val c: C) : Row

// Continues through Row22<A, B, C, ..., V> for up to 22 columns

Row Factory Functions

Convenient functions for creating rows with automatic type inference.

/**
 * Create a single-column row
 */
fun <A> row(a: A): Row1<A>

/**
 * Create a two-column row
 */
fun <A, B> row(a: A, b: B): Row2<A, B>

/**
 * Create a three-column row
 */
fun <A, B, C> row(a: A, b: B, c: C): Row3<A, B, C>

// Continues through row(a, b, ..., v) for up to 22 parameters

Table Headers

Type-safe header definitions that provide column labels for tables.

/**
 * Single-column headers
 */
data class Headers1(val labelA: String)

/**
 * Two-column headers
 */
data class Headers2(val labelA: String, val labelB: String)

/**
 * Three-column headers
 */
data class Headers3(val labelA: String, val labelB: String, val labelC: String)

// Continues through Headers22 for up to 22 columns

Header Factory Functions

Convenient functions for creating headers.

/**
 * Create single-column headers
 */
fun headers(a: String): Headers1

/**
 * Create two-column headers
 */
fun headers(a: String, b: String): Headers2

/**
 * Create three-column headers
 */
fun headers(a: String, b: String, c: String): Headers3

// Continues through headers(a, b, ..., v) for up to 22 parameters

Table Definitions

Type-safe table containers that combine headers with rows of data.

/**
 * Single-column table
 */
data class Table1<out A>(val headers: Headers1, val rows: List<Row1<A>>)

/**
 * Two-column table
 */
data class Table2<out A, out B>(val headers: Headers2, val rows: List<Row2<A, B>>)

/**
 * Three-column table
 */
data class Table3<out A, out B, out C>(val headers: Headers3, val rows: List<Row3<A, B, C>>)

// Continues through Table22 for up to 22 columns

Table Factory Functions

Functions for creating tables from headers and rows.

/**
 * Create table from headers and row list
 */
fun <A> table(headers: Headers1, rows: List<Row1<A>>): Table1<A>

/**
 * Create table from headers and vararg rows
 */
fun <A> table(headers: Headers1, vararg rows: Row1<A>): Table1<A>

/**
 * Create two-column table from headers and row list
 */
fun <A, B> table(headers: Headers2, rows: List<Row2<A, B>>): Table2<A, B>

/**
 * Create two-column table from headers and vararg rows
 */
fun <A, B> table(headers: Headers2, vararg rows: Row2<A, B>): Table2<A, B>

// Similar patterns continue for all table arities through Table22

Table Testing Functions

Execute tests against all rows in a table with full type safety.

/**
 * Execute test function for each row in single-column table
 */
suspend fun <A> Table1<A>.forAll(fn: suspend (A) -> Unit)

/**
 * Execute test function for each row in two-column table
 */
suspend fun <A, B> Table2<A, B>.forAll(fn: suspend (A, B) -> Unit)

/**
 * Execute test function for each row in three-column table
 */
suspend fun <A, B, C> Table3<A, B, C>.forAll(fn: suspend (A, B, C) -> Unit)

// Continues through Table9.forAll for up to 9-parameter test functions

/**
 * Assert that no rows in single-column table match the predicate
 */
suspend fun <A> Table1<A>.forNone(fn: suspend (A) -> Unit)

/**
 * Assert that no rows in two-column table match the predicate
 */
suspend fun <A, B> Table2<A, B>.forNone(fn: suspend (A, B) -> Unit)

// Similar patterns for forNone with other table arities

Standalone Testing Functions

Execute table tests without using extension functions.

/**
 * Test all rows in a single-column table
 */
suspend fun <A> forAll(table: Table1<A>, fn: suspend (A) -> Unit)

/**
 * Test all rows in a two-column table
 */
suspend fun <A, B> forAll(table: Table2<A, B>, fn: suspend (A, B) -> Unit)

// Continues through forAll for up to 9-parameter variants

/**
 * Assert no rows in single-column table match
 */
suspend fun <A> forNone(table: Table1<A>, fn: suspend (A) -> Unit)

/**
 * Assert no rows in two-column table match
 */
suspend fun <A, B> forNone(table: Table2<A, B>, fn: suspend (A, B) -> Unit)

// Similar patterns for forNone with other arities

Vararg Testing Functions

Execute tests directly with row data without creating tables first.

/**
 * Test all provided single-column rows
 */
suspend fun <A> forAll(vararg rows: Row1<A>, testfn: suspend (A) -> Unit)

/**
 * Test all provided two-column rows
 */
suspend fun <A, B> forAll(vararg rows: Row2<A, B>, testfn: suspend (A, B) -> Unit)

/**
 * Test all provided three-column rows
 */
suspend fun <A, B, C> forAll(vararg rows: Row3<A, B, C>, testfn: suspend (A, B, C) -> Unit)

// Continues through forAll for up to 22-parameter variants

/**
 * Assert that none of the provided single-column rows match
 */
suspend fun <A> forNone(vararg rows: Row1<A>, testfn: suspend (A) -> Unit)

/**
 * Assert that none of the provided two-column rows match
 */
suspend fun <A, B> forNone(vararg rows: Row2<A, B>, testfn: suspend (A, B) -> Unit)

// Similar patterns for forNone with other arities

Usage Examples

Basic Table Testing

import io.kotest.data.*
import io.kotest.matchers.shouldBe

// Create and test a simple table
val mathTable = table(
    headers("a", "b", "sum"),
    row(1, 2, 3),
    row(3, 4, 7),
    row(5, 6, 11),
    row(10, 20, 30)
)

mathTable.forAll { a, b, expected ->
    (a + b) shouldBe expected
}

String Processing Table

import io.kotest.data.*
import io.kotest.matchers.shouldBe

val stringTable = table(
    headers("input", "expected_length", "expected_uppercase"),
    row("hello", 5, "HELLO"),
    row("world", 5, "WORLD"),
    row("kotlin", 6, "KOTLIN"),
    row("", 0, "")
)

stringTable.forAll { input, expectedLength, expectedUpper ->
    input.length shouldBe expectedLength
    input.uppercase() shouldBe expectedUpper
}

Single Column Testing

import io.kotest.data.*
import io.kotest.matchers.shouldBe

val numbersTable = table(
    headers("number"),
    row(2),
    row(4), 
    row(6),
    row(8)
)

numbersTable.forAll { number ->
    (number % 2) shouldBe 0  // Assert all numbers are even
}

Complex Data Types

import io.kotest.data.*
import io.kotest.matchers.shouldBe

data class User(val name: String, val age: Int)
data class ValidationResult(val isValid: Boolean, val errors: List<String>)

val userValidationTable = table(
    headers("user", "expected_result"),
    row(
        User("Alice", 25), 
        ValidationResult(true, emptyList())
    ),
    row(
        User("", 25), 
        ValidationResult(false, listOf("Name cannot be empty"))
    ),
    row(
        User("Bob", -1), 
        ValidationResult(false, listOf("Age must be positive"))
    )
)

userValidationTable.forAll { user, expected ->
    val result = validateUser(user)  // Hypothetical validation function
    result shouldBe expected
}

Using forNone

import io.kotest.data.*
import io.kotest.matchers.shouldBe

val invalidInputsTable = table(
    headers("invalid_input"),
    row(""),
    row("   "),
    row("\t\n"),
    row(null)
)

// Assert that none of these inputs are valid
invalidInputsTable.forNone { input ->
    isValidInput(input) shouldBe true  // This should fail for all rows
}

Large Tables with Many Columns

import io.kotest.data.*
import io.kotest.matchers.shouldBe

// Example with 5 columns
val productTable = table(
    headers("name", "price", "category", "inStock", "rating"),
    row("Laptop", 999.99, "Electronics", true, 4.5),
    row("Book", 19.99, "Literature", true, 4.2),
    row("Phone", 699.99, "Electronics", false, 4.8)
)

productTable.forAll { name, price, category, inStock, rating ->
    name.isNotEmpty() shouldBe true
    price shouldBe greaterThan(0.0)
    category.isNotEmpty() shouldBe true
    rating shouldBe between(0.0, 5.0)
}

Using Vararg Testing Functions

import io.kotest.data.*
import io.kotest.matchers.shouldBe

// Test directly with rows without creating a table first
forAll(
    row("apple", 5),
    row("banana", 6),
    row("cherry", 6),
    row("date", 4)
) { fruit, expectedLength ->
    fruit.length shouldBe expectedLength
}

// Test that none of these calculations are correct
forNone(
    row(2, 2, 5),  // 2 + 2 ≠ 5
    row(3, 3, 7),  // 3 + 3 ≠ 7
    row(4, 4, 9)   // 4 + 4 ≠ 9
) { a, b, wrongSum ->
    (a + b) shouldBe wrongSum  // All should fail
}

// Single column vararg testing
forAll(
    row(2),
    row(4),
    row(6),
    row(8)
) { number ->
    (number % 2) shouldBe 0  // All should be even
}

Type Safety Benefits

The data-driven testing framework maintains full type safety throughout:

// Type inference works automatically
val table = table(
    headers("number", "text"),
    row(42, "hello"),        // Inferred as Table2<Int, String>
    row(100, "world")
)

table.forAll { number, text ->
    // number is Int, text is String - fully typed
    number + text.length // Compiles correctly
}

Performance Considerations

  • Suspend Functions: All testing functions are suspending to support async operations
  • Type Erasure: Tables maintain runtime type information where possible
  • Memory Efficiency: Large tables are processed row-by-row, not loaded entirely into memory
  • Parallel Execution: Individual row tests can be parallelized when appropriate

Install with Tessl CLI

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

docs

collection-inspectors.md

core-matchers.md

data-driven-testing.md

error-handling.md

index.md

tile.json