or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdasserter.mdbasic-assertions.mdcollection-assertions.mdexception-testing.mdindex.mdtype-null-assertions.md

exception-testing.mddocs/

0

# Exception and Failure Testing

1

2

Assertions for testing exception handling, failure scenarios, and expected error conditions. Essential for robust testing of error paths and edge cases.

3

4

## Capabilities

5

6

### Failure Assertions

7

8

#### fail Function

9

10

Marks a test as failed immediately with an optional message and cause.

11

12

```kotlin { .api }

13

/**

14

* Marks a test as having failed if this point in the execution path is reached

15

* @param message - Optional failure message

16

* @return Nothing (function never returns)

17

*/

18

fun fail(message: String? = null): Nothing

19

20

/**

21

* Marks a test as having failed with an optional message and cause exception

22

* @param message - Optional failure message

23

* @param cause - Optional exception that caused the failure

24

* @return Nothing (function never returns)

25

*/

26

fun fail(message: String? = null, cause: Throwable? = null): Nothing

27

```

28

29

**Usage Examples:**

30

31

```kotlin

32

import kotlin.test.*

33

34

@Test

35

fun testConditionalFailure() {

36

val result = performCriticalOperation()

37

38

when (result.status) {

39

Status.SUCCESS -> {

40

// Test passes, continue validation

41

assertTrue(result.data.isNotEmpty())

42

}

43

Status.RECOVERABLE_ERROR -> {

44

// Expected failure scenario

45

assertTrue(result.canRetry)

46

}

47

Status.FATAL_ERROR -> {

48

fail("Critical operation failed: ${result.errorMessage}")

49

}

50

else -> {

51

fail("Unexpected status: ${result.status}")

52

}

53

}

54

}

55

56

@Test

57

fun testWithCause() {

58

try {

59

val connection = establishConnection()

60

if (connection == null) {

61

fail("Failed to establish connection")

62

}

63

} catch (e: NetworkException) {

64

fail("Network error during connection", e)

65

}

66

}

67

```

68

69

### Exception Assertions

70

71

#### assertFails Function

72

73

Asserts that a block of code throws any exception.

74

75

```kotlin { .api }

76

/**

77

* Asserts that given function block fails by throwing an exception

78

* @param block - Code block that should throw an exception

79

* @return The exception that was thrown

80

*/

81

inline fun assertFails(block: () -> Unit): Throwable

82

83

/**

84

* Asserts that given function block fails by throwing an exception

85

* @param message - Optional message used as prefix for failure message

86

* @param block - Code block that should throw an exception

87

* @return The exception that was thrown

88

*/

89

inline fun assertFails(message: String?, block: () -> Unit): Throwable

90

```

91

92

**Usage Examples:**

93

94

```kotlin

95

@Test

96

fun testExceptionHandling() {

97

// Test that division by zero throws

98

val exception = assertFails {

99

val result = 10 / 0

100

}

101

assertTrue(exception is ArithmeticException)

102

103

// Test with custom message

104

val parseException = assertFails("Should fail to parse invalid number") {

105

"not-a-number".toInt()

106

}

107

assertTrue(parseException is NumberFormatException)

108

109

// Inspect exception details

110

val validationException = assertFails {

111

validateEmail("invalid-email")

112

}

113

assertContains(validationException.message ?: "", "email")

114

}

115

116

@Test

117

fun testApiErrorHandling() {

118

val client = ApiClient()

119

120

// Test unauthorized access

121

val authException = assertFails("Should fail with invalid token") {

122

client.makeRequest("/protected", token = "invalid")

123

}

124

125

// Verify we can inspect the returned exception

126

assertTrue(authException.message?.contains("unauthorized") == true)

127

}

128

```

129

130

#### assertFailsWith Function

131

132

Asserts that a block of code throws a specific type of exception.

133

134

```kotlin { .api }

135

/**

136

* Asserts that a block fails with a specific exception of type T

137

* @param message - Optional message used as prefix for failure message

138

* @param block - Code block that should throw exception of type T

139

* @return Exception of the expected type T

140

*/

141

inline fun <reified T : Throwable> assertFailsWith(message: String? = null, block: () -> Unit): T

142

143

/**

144

* Asserts that a block fails with a specific exception of the given class

145

* @param exceptionClass - Expected exception class

146

* @param block - Code block that should throw the exception

147

* @return Exception of the expected type T

148

*/

149

inline fun <T : Throwable> assertFailsWith(exceptionClass: KClass<T>, block: () -> Unit): T

150

151

/**

152

* Asserts that a block fails with a specific exception of the given class

153

* @param exceptionClass - Expected exception class

154

* @param message - Optional message used as prefix for failure message

155

* @param block - Code block that should throw the exception

156

* @return Exception of the expected type T

157

*/

158

inline fun <T : Throwable> assertFailsWith(exceptionClass: KClass<T>, message: String?, block: () -> Unit): T

159

```

160

161

**Usage Examples:**

162

163

```kotlin

164

@Test

165

fun testSpecificExceptions() {

166

// Test specific exception type with reified generic

167

val illegalArg = assertFailsWith<IllegalArgumentException> {

168

validateAge(-5)

169

}

170

assertEquals("Age cannot be negative", illegalArg.message)

171

172

// Test with custom message

173

val nullPointer = assertFailsWith<NullPointerException>("Should throw NPE for null input") {

174

processData(null)

175

}

176

177

// Test with KClass parameter

178

val ioException = assertFailsWith(IOException::class) {

179

readFile("nonexistent.txt")

180

}

181

assertContains(ioException.message ?: "", "nonexistent.txt")

182

}

183

184

@Test

185

fun testCustomExceptions() {

186

class ValidationException(message: String) : Exception(message)

187

188

val validationError = assertFailsWith<ValidationException> {

189

validateUserInput(UserInput(name = "", email = "invalid"))

190

}

191

192

assertContains(validationError.message ?: "", "name")

193

assertContains(validationError.message ?: "", "email")

194

}

195

196

@Test

197

fun testExceptionHierarchy() {

198

// Test that we catch the specific subtype

199

val specificException = assertFailsWith<FileNotFoundException> {

200

openFile("missing.txt")

201

}

202

203

// FileNotFoundException is also an IOException

204

assertTrue(specificException is IOException)

205

}

206

```

207

208

### Expected Value Testing

209

210

#### expect Function

211

212

Asserts that a block returns the expected value (convenience function combining execution and assertion).

213

214

```kotlin { .api }

215

/**

216

* Asserts that given function block returns the given expected value

217

* @param expected - Expected return value

218

* @param block - Function that should return the expected value

219

*/

220

inline fun <T> expect(expected: T, block: () -> T)

221

222

/**

223

* Asserts that given function block returns the given expected value with custom message

224

* @param expected - Expected return value

225

* @param message - Optional custom failure message

226

* @param block - Function that should return the expected value

227

*/

228

inline fun <T> expect(expected: T, message: String?, block: () -> T)

229

```

230

231

**Usage Examples:**

232

233

```kotlin

234

@Test

235

fun testExpectedValues() {

236

// Simple calculation

237

expect(25) { 5 * 5 }

238

239

// String operations

240

expect("HELLO") { "hello".uppercase() }

241

242

// With custom message

243

expect(true, "User should be authenticated") {

244

authenticateUser("admin", "password123")

245

}

246

247

// Complex operations

248

expect(listOf(2, 4, 6)) {

249

listOf(1, 2, 3).map { it * 2 }

250

}

251

}

252

253

@Test

254

fun testFunctionResults() {

255

// Test pure functions

256

expect(42) { fibonacci(9) } // 9th Fibonacci number

257

258

// Test with side effects

259

val cache = mutableMapOf<String, Int>()

260

expect(5) {

261

cache.computeIfAbsent("key") { 5 }

262

}

263

264

// Verify side effect occurred

265

assertTrue(cache.containsKey("key"))

266

}

267

```

268

269

## Advanced Exception Testing Patterns

270

271

### Testing Exception Chains

272

273

```kotlin

274

@Test

275

fun testExceptionChaining() {

276

class DatabaseException(message: String, cause: Throwable) : Exception(message, cause)

277

class ServiceException(message: String, cause: Throwable) : Exception(message, cause)

278

279

val serviceException = assertFailsWith<ServiceException> {

280

try {

281

// Simulate database failure

282

throw SQLException("Connection timeout")

283

} catch (e: SQLException) {

284

throw DatabaseException("Database operation failed", e)

285

} catch (e: DatabaseException) {

286

throw ServiceException("Service unavailable", e)

287

}

288

}

289

290

// Verify exception chain

291

assertNotNull(serviceException.cause)

292

assertTrue(serviceException.cause is DatabaseException)

293

294

val dbException = serviceException.cause as DatabaseException

295

assertNotNull(dbException.cause)

296

assertTrue(dbException.cause is SQLException)

297

298

val sqlException = dbException.cause as SQLException

299

assertEquals("Connection timeout", sqlException.message)

300

}

301

```

302

303

### Testing Resource Management

304

305

```kotlin

306

@Test

307

fun testResourceCleanup() {

308

class Resource : AutoCloseable {

309

var closed = false

310

override fun close() { closed = true }

311

}

312

313

val resource = Resource()

314

315

// Test that exception doesn't prevent cleanup

316

assertFailsWith<IllegalStateException> {

317

try {

318

resource.use {

319

throw IllegalStateException("Simulated error")

320

}

321

} finally {

322

assertTrue(resource.closed, "Resource should be closed even after exception")

323

}

324

}

325

}

326

```

327

328

### Testing Validation Logic

329

330

```kotlin

331

@Test

332

fun testInputValidation() {

333

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

334

335

fun validateUser(user: User) {

336

require(user.name.isNotBlank()) { "Name cannot be blank" }

337

require(user.email.contains("@")) { "Invalid email format" }

338

require(user.age >= 0) { "Age cannot be negative" }

339

}

340

341

// Test each validation rule

342

assertFailsWith<IllegalArgumentException>("Should reject blank name") {

343

validateUser(User("", "test@example.com", 25))

344

}

345

346

assertFailsWith<IllegalArgumentException>("Should reject invalid email") {

347

validateUser(User("John", "invalid-email", 25))

348

}

349

350

assertFailsWith<IllegalArgumentException>("Should reject negative age") {

351

validateUser(User("John", "john@example.com", -1))

352

}

353

354

// Test valid input doesn't throw

355

assertDoesNotThrow {

356

validateUser(User("John", "john@example.com", 25))

357

}

358

}

359

360

// Helper function for readability

361

inline fun assertDoesNotThrow(block: () -> Unit) {

362

try {

363

block()

364

} catch (e: Exception) {

365

fail("Expected no exception, but got: ${e::class.simpleName}: ${e.message}")

366

}

367

}

368

```

369

370

### Testing Timeout and Async Operations

371

372

```kotlin

373

@Test

374

fun testTimeoutBehavior() {

375

// Test that long-running operation times out

376

val timeoutException = assertFailsWith<TimeoutException> {

377

runWithTimeout(100) { // 100ms timeout

378

Thread.sleep(1000) // Sleep for 1 second

379

}

380

}

381

382

assertContains(timeoutException.message ?: "", "timeout")

383

}

384

385

@Test

386

fun testAsyncExceptions() {

387

val future = CompletableFuture<String>()

388

389

// Complete with exception

390

future.completeExceptionally(RuntimeException("Async failure"))

391

392

val executionException = assertFailsWith<ExecutionException> {

393

future.get()

394

}

395

396

assertTrue(executionException.cause is RuntimeException)

397

assertEquals("Async failure", executionException.cause?.message)

398

}

399

```

400

401

### Testing State Transitions

402

403

```kotlin

404

@Test

405

fun testStateMachine() {

406

enum class State { CREATED, STARTED, STOPPED, DISPOSED }

407

408

class StateMachine {

409

private var state = State.CREATED

410

411

fun start() {

412

require(state == State.CREATED) { "Can only start from CREATED state" }

413

state = State.STARTED

414

}

415

416

fun stop() {

417

require(state == State.STARTED) { "Can only stop from STARTED state" }

418

state = State.STOPPED

419

}

420

421

fun dispose() {

422

require(state == State.STOPPED) { "Can only dispose from STOPPED state" }

423

state = State.DISPOSED

424

}

425

}

426

427

val machine = StateMachine()

428

429

// Test invalid transitions

430

assertFailsWith<IllegalArgumentException>("Should not stop before starting") {

431

machine.stop()

432

}

433

434

assertFailsWith<IllegalArgumentException>("Should not dispose before stopping") {

435

machine.dispose()

436

}

437

438

// Test valid sequence

439

machine.start()

440

machine.stop()

441

machine.dispose()

442

443

// Test invalid transition after disposal

444

assertFailsWith<IllegalArgumentException>("Should not restart after disposal") {

445

machine.start()

446

}

447

}

448

```

449

450

### Test Code Utilities

451

452

#### todo Function

453

454

Marks unimplemented test code that should be skipped but kept referenced in the codebase.

455

456

```kotlin { .api }

457

/**

458

* Takes the given block of test code and doesn't execute it

459

* This keeps the code under test referenced, but doesn't actually test it until implemented

460

* @param block - Test code block to skip execution

461

*/

462

fun todo(block: () -> Unit)

463

```

464

465

**Usage Examples:**

466

467

```kotlin

468

import kotlin.test.*

469

470

@Test

471

fun testComplexFeature() {

472

// Test implemented part

473

val basicResult = performBasicOperation()

474

assertEquals("expected", basicResult)

475

476

// Skip unimplemented test but keep it in code

477

todo {

478

// This test code won't execute but stays in the codebase

479

val advancedResult = performAdvancedOperation()

480

assertEquals("advanced", advancedResult)

481

assertNotNull(advancedResult.metadata)

482

}

483

}

484

485

@Test

486

fun testTodoWithDescription() {

487

// Test current implementation

488

val service = createService()

489

assertTrue(service.isReady())

490

491

// TODO: Add comprehensive integration tests

492

todo {

493

// Integration test placeholder - won't run

494

val integrationResult = service.performIntegration()

495

assertTrue(integrationResult.success)

496

assertContains(integrationResult.logs, "Integration completed")

497

}

498

}

499

500

class IncrementalTestDevelopment {

501

@Test

502

fun testBasicFunctionality() {

503

// Implemented test

504

val calculator = Calculator()

505

assertEquals(4, calculator.add(2, 2))

506

}

507

508

@Test

509

fun testAdvancedFunctionality() {

510

todo {

511

// Placeholder for future advanced tests

512

val calculator = Calculator()

513

assertEquals(8.0, calculator.power(2.0, 3.0), 0.001)

514

assertEquals(2.0, calculator.sqrt(4.0), 0.001)

515

}

516

}

517

}

518

```

519

520

**Platform Behavior:**

521

- **JVM**: Prints "TODO at [stack trace location]" to console

522

- **JavaScript**: Prints "TODO at [function reference]" to console

523

- **Native/WASM**: Prints "TODO at [function reference]" to console

524

525

The `todo` function is particularly useful during test-driven development when you want to outline test cases but implement them incrementally, ensuring unimplemented tests don't cause failures while keeping them visible in the codebase.

526

527

## Types

528

529

```kotlin { .api }

530

/**

531

* Represents a Kotlin class for type-safe exception assertions

532

*/

533

typealias KClass<T> = kotlin.reflect.KClass<T>

534

```