or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

collection-inspectors.mdcore-matchers.mddata-driven-testing.mderror-handling.mdindex.md

data-driven-testing.mddocs/

0

# Data-Driven Testing

1

2

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.

3

4

## Capabilities

5

6

### Row Definitions

7

8

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

9

10

```kotlin { .api }

11

/**

12

* Base interface for all row types

13

*/

14

interface Row {

15

fun values(): List<Any?>

16

}

17

18

/**

19

* Single-column row

20

*/

21

data class Row1<out A>(val a: A) : Row

22

23

/**

24

* Two-column row

25

*/

26

data class Row2<out A, out B>(val a: A, val b: B) : Row

27

28

/**

29

* Three-column row

30

*/

31

data class Row3<out A, out B, out C>(val a: A, val b: B, val c: C) : Row

32

33

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

34

```

35

36

### Row Factory Functions

37

38

Convenient functions for creating rows with automatic type inference.

39

40

```kotlin { .api }

41

/**

42

* Create a single-column row

43

*/

44

fun <A> row(a: A): Row1<A>

45

46

/**

47

* Create a two-column row

48

*/

49

fun <A, B> row(a: A, b: B): Row2<A, B>

50

51

/**

52

* Create a three-column row

53

*/

54

fun <A, B, C> row(a: A, b: B, c: C): Row3<A, B, C>

55

56

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

57

```

58

59

### Table Headers

60

61

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

62

63

```kotlin { .api }

64

/**

65

* Single-column headers

66

*/

67

data class Headers1(val labelA: String)

68

69

/**

70

* Two-column headers

71

*/

72

data class Headers2(val labelA: String, val labelB: String)

73

74

/**

75

* Three-column headers

76

*/

77

data class Headers3(val labelA: String, val labelB: String, val labelC: String)

78

79

// Continues through Headers22 for up to 22 columns

80

```

81

82

### Header Factory Functions

83

84

Convenient functions for creating headers.

85

86

```kotlin { .api }

87

/**

88

* Create single-column headers

89

*/

90

fun headers(a: String): Headers1

91

92

/**

93

* Create two-column headers

94

*/

95

fun headers(a: String, b: String): Headers2

96

97

/**

98

* Create three-column headers

99

*/

100

fun headers(a: String, b: String, c: String): Headers3

101

102

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

103

```

104

105

### Table Definitions

106

107

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

108

109

```kotlin { .api }

110

/**

111

* Single-column table

112

*/

113

data class Table1<out A>(val headers: Headers1, val rows: List<Row1<A>>)

114

115

/**

116

* Two-column table

117

*/

118

data class Table2<out A, out B>(val headers: Headers2, val rows: List<Row2<A, B>>)

119

120

/**

121

* Three-column table

122

*/

123

data class Table3<out A, out B, out C>(val headers: Headers3, val rows: List<Row3<A, B, C>>)

124

125

// Continues through Table22 for up to 22 columns

126

```

127

128

### Table Factory Functions

129

130

Functions for creating tables from headers and rows.

131

132

```kotlin { .api }

133

/**

134

* Create table from headers and row list

135

*/

136

fun <A> table(headers: Headers1, rows: List<Row1<A>>): Table1<A>

137

138

/**

139

* Create table from headers and vararg rows

140

*/

141

fun <A> table(headers: Headers1, vararg rows: Row1<A>): Table1<A>

142

143

/**

144

* Create two-column table from headers and row list

145

*/

146

fun <A, B> table(headers: Headers2, rows: List<Row2<A, B>>): Table2<A, B>

147

148

/**

149

* Create two-column table from headers and vararg rows

150

*/

151

fun <A, B> table(headers: Headers2, vararg rows: Row2<A, B>): Table2<A, B>

152

153

// Similar patterns continue for all table arities through Table22

154

```

155

156

### Table Testing Functions

157

158

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

159

160

```kotlin { .api }

161

/**

162

* Execute test function for each row in single-column table

163

*/

164

suspend fun <A> Table1<A>.forAll(fn: suspend (A) -> Unit)

165

166

/**

167

* Execute test function for each row in two-column table

168

*/

169

suspend fun <A, B> Table2<A, B>.forAll(fn: suspend (A, B) -> Unit)

170

171

/**

172

* Execute test function for each row in three-column table

173

*/

174

suspend fun <A, B, C> Table3<A, B, C>.forAll(fn: suspend (A, B, C) -> Unit)

175

176

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

177

178

/**

179

* Assert that no rows in single-column table match the predicate

180

*/

181

suspend fun <A> Table1<A>.forNone(fn: suspend (A) -> Unit)

182

183

/**

184

* Assert that no rows in two-column table match the predicate

185

*/

186

suspend fun <A, B> Table2<A, B>.forNone(fn: suspend (A, B) -> Unit)

187

188

// Similar patterns for forNone with other table arities

189

```

190

191

### Standalone Testing Functions

192

193

Execute table tests without using extension functions.

194

195

```kotlin { .api }

196

/**

197

* Test all rows in a single-column table

198

*/

199

suspend fun <A> forAll(table: Table1<A>, fn: suspend (A) -> Unit)

200

201

/**

202

* Test all rows in a two-column table

203

*/

204

suspend fun <A, B> forAll(table: Table2<A, B>, fn: suspend (A, B) -> Unit)

205

206

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

207

208

/**

209

* Assert no rows in single-column table match

210

*/

211

suspend fun <A> forNone(table: Table1<A>, fn: suspend (A) -> Unit)

212

213

/**

214

* Assert no rows in two-column table match

215

*/

216

suspend fun <A, B> forNone(table: Table2<A, B>, fn: suspend (A, B) -> Unit)

217

218

// Similar patterns for forNone with other arities

219

```

220

221

### Vararg Testing Functions

222

223

Execute tests directly with row data without creating tables first.

224

225

```kotlin { .api }

226

/**

227

* Test all provided single-column rows

228

*/

229

suspend fun <A> forAll(vararg rows: Row1<A>, testfn: suspend (A) -> Unit)

230

231

/**

232

* Test all provided two-column rows

233

*/

234

suspend fun <A, B> forAll(vararg rows: Row2<A, B>, testfn: suspend (A, B) -> Unit)

235

236

/**

237

* Test all provided three-column rows

238

*/

239

suspend fun <A, B, C> forAll(vararg rows: Row3<A, B, C>, testfn: suspend (A, B, C) -> Unit)

240

241

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

242

243

/**

244

* Assert that none of the provided single-column rows match

245

*/

246

suspend fun <A> forNone(vararg rows: Row1<A>, testfn: suspend (A) -> Unit)

247

248

/**

249

* Assert that none of the provided two-column rows match

250

*/

251

suspend fun <A, B> forNone(vararg rows: Row2<A, B>, testfn: suspend (A, B) -> Unit)

252

253

// Similar patterns for forNone with other arities

254

```

255

256

## Usage Examples

257

258

### Basic Table Testing

259

260

```kotlin

261

import io.kotest.data.*

262

import io.kotest.matchers.shouldBe

263

264

// Create and test a simple table

265

val mathTable = table(

266

headers("a", "b", "sum"),

267

row(1, 2, 3),

268

row(3, 4, 7),

269

row(5, 6, 11),

270

row(10, 20, 30)

271

)

272

273

mathTable.forAll { a, b, expected ->

274

(a + b) shouldBe expected

275

}

276

```

277

278

### String Processing Table

279

280

```kotlin

281

import io.kotest.data.*

282

import io.kotest.matchers.shouldBe

283

284

val stringTable = table(

285

headers("input", "expected_length", "expected_uppercase"),

286

row("hello", 5, "HELLO"),

287

row("world", 5, "WORLD"),

288

row("kotlin", 6, "KOTLIN"),

289

row("", 0, "")

290

)

291

292

stringTable.forAll { input, expectedLength, expectedUpper ->

293

input.length shouldBe expectedLength

294

input.uppercase() shouldBe expectedUpper

295

}

296

```

297

298

### Single Column Testing

299

300

```kotlin

301

import io.kotest.data.*

302

import io.kotest.matchers.shouldBe

303

304

val numbersTable = table(

305

headers("number"),

306

row(2),

307

row(4),

308

row(6),

309

row(8)

310

)

311

312

numbersTable.forAll { number ->

313

(number % 2) shouldBe 0 // Assert all numbers are even

314

}

315

```

316

317

### Complex Data Types

318

319

```kotlin

320

import io.kotest.data.*

321

import io.kotest.matchers.shouldBe

322

323

data class User(val name: String, val age: Int)

324

data class ValidationResult(val isValid: Boolean, val errors: List<String>)

325

326

val userValidationTable = table(

327

headers("user", "expected_result"),

328

row(

329

User("Alice", 25),

330

ValidationResult(true, emptyList())

331

),

332

row(

333

User("", 25),

334

ValidationResult(false, listOf("Name cannot be empty"))

335

),

336

row(

337

User("Bob", -1),

338

ValidationResult(false, listOf("Age must be positive"))

339

)

340

)

341

342

userValidationTable.forAll { user, expected ->

343

val result = validateUser(user) // Hypothetical validation function

344

result shouldBe expected

345

}

346

```

347

348

### Using forNone

349

350

```kotlin

351

import io.kotest.data.*

352

import io.kotest.matchers.shouldBe

353

354

val invalidInputsTable = table(

355

headers("invalid_input"),

356

row(""),

357

row(" "),

358

row("\t\n"),

359

row(null)

360

)

361

362

// Assert that none of these inputs are valid

363

invalidInputsTable.forNone { input ->

364

isValidInput(input) shouldBe true // This should fail for all rows

365

}

366

```

367

368

### Large Tables with Many Columns

369

370

```kotlin

371

import io.kotest.data.*

372

import io.kotest.matchers.shouldBe

373

374

// Example with 5 columns

375

val productTable = table(

376

headers("name", "price", "category", "inStock", "rating"),

377

row("Laptop", 999.99, "Electronics", true, 4.5),

378

row("Book", 19.99, "Literature", true, 4.2),

379

row("Phone", 699.99, "Electronics", false, 4.8)

380

)

381

382

productTable.forAll { name, price, category, inStock, rating ->

383

name.isNotEmpty() shouldBe true

384

price shouldBe greaterThan(0.0)

385

category.isNotEmpty() shouldBe true

386

rating shouldBe between(0.0, 5.0)

387

}

388

```

389

390

### Using Vararg Testing Functions

391

392

```kotlin

393

import io.kotest.data.*

394

import io.kotest.matchers.shouldBe

395

396

// Test directly with rows without creating a table first

397

forAll(

398

row("apple", 5),

399

row("banana", 6),

400

row("cherry", 6),

401

row("date", 4)

402

) { fruit, expectedLength ->

403

fruit.length shouldBe expectedLength

404

}

405

406

// Test that none of these calculations are correct

407

forNone(

408

row(2, 2, 5), // 2 + 2 ≠ 5

409

row(3, 3, 7), // 3 + 3 ≠ 7

410

row(4, 4, 9) // 4 + 4 ≠ 9

411

) { a, b, wrongSum ->

412

(a + b) shouldBe wrongSum // All should fail

413

}

414

415

// Single column vararg testing

416

forAll(

417

row(2),

418

row(4),

419

row(6),

420

row(8)

421

) { number ->

422

(number % 2) shouldBe 0 // All should be even

423

}

424

```

425

426

## Type Safety Benefits

427

428

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

429

430

```kotlin

431

// Type inference works automatically

432

val table = table(

433

headers("number", "text"),

434

row(42, "hello"), // Inferred as Table2<Int, String>

435

row(100, "world")

436

)

437

438

table.forAll { number, text ->

439

// number is Int, text is String - fully typed

440

number + text.length // Compiles correctly

441

}

442

```

443

444

## Performance Considerations

445

446

- **Suspend Functions**: All testing functions are suspending to support async operations

447

- **Type Erasure**: Tables maintain runtime type information where possible

448

- **Memory Efficiency**: Large tables are processed row-by-row, not loaded entirely into memory

449

- **Parallel Execution**: Individual row tests can be parallelized when appropriate