or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdconfiguration.mdexceptions.mdfixtures.mdindex.mdtest-suites.mdtransforms.md

exceptions.mddocs/

0

# Exception Handling

1

2

Comprehensive exception classes for test failures with IDE integration and detailed error reporting. MUnit provides specialized exception types that work well with development tools and provide actionable error messages.

3

4

## Capabilities

5

6

### FailException - Basic Test Failure

7

8

The primary exception type for test failures with support for custom messages, causes, and location tracking.

9

10

```scala { .api }

11

/**

12

* Primary exception for test failures

13

* @param message The failure message describing what went wrong

14

* @param cause The underlying exception that caused the failure (optional)

15

* @param isStackTracesEnabled Whether to include stack traces in output

16

* @param location Source code location where the failure occurred

17

*/

18

class FailException(

19

val message: String,

20

val cause: Throwable,

21

val isStackTracesEnabled: Boolean,

22

val location: Location

23

) extends AssertionError(message, cause) with FailExceptionLike[FailException] {

24

25

/** Create a new exception with a different message */

26

def withMessage(newMessage: String): FailException

27

}

28

```

29

30

**Usage Examples:**

31

32

```scala

33

class ExceptionExamples extends FunSuite {

34

test("explicit failure") {

35

val result = performOperation()

36

if (!result.isValid) {

37

throw new FailException(

38

s"Operation failed: ${result.error}",

39

result.cause,

40

true,

41

Location.empty

42

)

43

}

44

}

45

46

test("conditional failure with context") {

47

val data = processData()

48

if (data.isEmpty) {

49

fail("No data was processed") // This throws FailException internally

50

}

51

}

52

}

53

```

54

55

### ComparisonFailException - IDE-Compatible Comparison Failures

56

57

Specialized exception for comparison failures that integrates with IDE diff viewers and provides structured comparison data.

58

59

```scala { .api }

60

/**

61

* Exception for comparison failures with IDE integration support

62

* Extends JUnit's ComparisonFailure for IDE compatibility

63

*/

64

class ComparisonFailException(

65

val message: String,

66

val obtained: Any,

67

val expected: Any,

68

val obtainedString: String,

69

val expectedString: String,

70

val location: Location

71

) extends ComparisonFailure(message, expectedString, obtainedString) with FailExceptionLike[ComparisonFailException] {

72

73

/** Create a new exception with a different message */

74

def withMessage(newMessage: String): ComparisonFailException

75

}

76

```

77

78

**Usage Examples:**

79

80

This exception is typically thrown automatically by assertion methods like `assertEquals`, but can be used directly:

81

82

```scala

83

class ComparisonExamples extends FunSuite {

84

test("assertEquals throws ComparisonFailException") {

85

val obtained = List(1, 2, 3)

86

val expected = List(1, 2, 4)

87

88

// This will throw ComparisonFailException, which IDEs can display as a diff

89

assertEquals(obtained, expected)

90

}

91

92

test("manual comparison failure") {

93

val result = parseJson(input)

94

val expected = ExpectedResult(...)

95

96

if (result != expected) {

97

throw new ComparisonFailException(

98

"JSON parsing mismatch",

99

result,

100

expected,

101

result.toString,

102

expected.toString,

103

Location.empty

104

)

105

}

106

}

107

}

108

```

109

110

### FailSuiteException - Suite-Level Failures

111

112

Exception type for failures that should abort the entire test suite rather than just a single test.

113

114

```scala { .api }

115

/**

116

* Exception that aborts the entire test suite

117

* @param message The failure message explaining why the suite was aborted

118

* @param location Source code location where the suite failure occurred

119

*/

120

class FailSuiteException(

121

override val message: String,

122

override val location: Location

123

) extends FailException(message, location) {

124

125

/** Create a new exception with a different message */

126

def withMessage(newMessage: String): FailSuiteException

127

}

128

```

129

130

**Usage Examples:**

131

132

```scala

133

class SuiteFailureExamples extends FunSuite {

134

override def beforeAll(): Unit = {

135

val dbConnection = connectToDatabase()

136

if (!dbConnection.isValid) {

137

failSuite("Cannot connect to test database")

138

// This throws FailSuiteException and stops all tests

139

}

140

}

141

142

test("this test won't run if suite failed") {

143

// This test is skipped if beforeAll() threw FailSuiteException

144

assert(true)

145

}

146

}

147

```

148

149

### FailExceptionLike - Common Exception Behavior

150

151

Trait that provides common functionality for all MUnit failure exceptions.

152

153

```scala { .api }

154

/**

155

* Common behavior for all MUnit failure exceptions

156

* @tparam T The concrete exception type

157

*/

158

trait FailExceptionLike[T <: AssertionError] extends Serializable {

159

160

/** Create a new exception with a different message */

161

def withMessage(message: String): T

162

163

/** Transform the message using a function */

164

def updateMessage(f: String => String): T

165

166

/** Source code location where the failure occurred */

167

def location: Location

168

169

/** Whether stack traces are enabled for this exception */

170

def isStackTracesEnabled: Boolean

171

}

172

```

173

174

### Exception Utilities

175

176

Utility functions for working with exceptions and error handling.

177

178

```scala { .api }

179

/**

180

* Utilities for exception handling

181

*/

182

object Exceptions {

183

/**

184

* Find the root cause of a nested exception

185

* @param x The exception to unwrap

186

* @return The deepest cause in the exception chain

187

*/

188

def rootCause(x: Throwable): Throwable

189

}

190

```

191

192

**Usage Examples:**

193

194

```scala

195

class ExceptionUtilityExamples extends FunSuite {

196

test("finding root cause") {

197

val rootCause = new IllegalArgumentException("Invalid input")

198

val wrapped = new RuntimeException("Processing failed", rootCause)

199

val nested = new Exception("Operation failed", wrapped)

200

201

val actual = Exceptions.rootCause(nested)

202

assertEquals(actual, rootCause)

203

assertEquals(actual.getMessage, "Invalid input")

204

}

205

}

206

```

207

208

### ComparisonFailExceptionHandler - Custom Exception Handling

209

210

Interface for customizing how comparison failures are handled and reported.

211

212

```scala { .api }

213

/**

214

* Handler for customizing comparison failure behavior

215

*/

216

trait ComparisonFailExceptionHandler {

217

/**

218

* Handle a comparison failure with custom logic

219

* @param message The failure message

220

* @param obtained String representation of the obtained value

221

* @param expected String representation of the expected value

222

* @param location Source code location of the failure

223

* @return Never returns (always throws an exception)

224

*/

225

def handle(

226

message: String,

227

obtained: String,

228

expected: String,

229

location: Location

230

): Nothing

231

}

232

```

233

234

### Special Exception Types

235

236

Additional exception types for specific testing scenarios.

237

238

```scala { .api }

239

/**

240

* Exception for flaky test failures (internal use)

241

* Extends FailException but with NoStackTrace for cleaner output

242

*/

243

class FlakyFailure(error: Throwable) extends FailException with NoStackTrace with Serializable

244

```

245

246

## Error Handling Patterns

247

248

### Graceful Failure Handling

249

250

```scala

251

class ErrorHandlingExamples extends FunSuite {

252

test("graceful failure with context") {

253

val input = getUserInput()

254

255

try {

256

val result = processInput(input)

257

assertEquals(result.status, "success")

258

} catch {

259

case e: ProcessingException =>

260

fail(

261

s"Processing failed for input: $input",

262

e

263

)

264

}

265

}

266

267

test("assertion with debugging context") {

268

val users = getUsers()

269

val activeUsers = users.filter(_.isActive)

270

271

assert(

272

activeUsers.nonEmpty,

273

s"Expected active users but got: ${users.map(u => s"${u.name}(${u.status})").mkString(", ")}"

274

)

275

}

276

}

277

```

278

279

### Suite-Level Error Handling

280

281

```scala

282

class DatabaseSuite extends FunSuite {

283

private var database: Database = _

284

285

override def beforeAll(): Unit = {

286

try {

287

database = Database.connect(testConfig)

288

database.migrate()

289

} catch {

290

case e: SQLException =>

291

failSuite(s"Failed to setup test database: ${e.getMessage}")

292

}

293

}

294

295

override def afterAll(): Unit = {

296

try {

297

database.close()

298

} catch {

299

case e: SQLException =>

300

// Log but don't fail suite during cleanup

301

println(s"Warning: Failed to close database: ${e.getMessage}")

302

}

303

}

304

}

305

```

306

307

### Custom Assertion Methods

308

309

```scala

310

object CustomAssertions {

311

def assertValidEmail(email: String)(implicit loc: Location): Unit = {

312

if (!email.contains("@")) {

313

throw new FailException(

314

s"Invalid email format: '$email' (missing @)",

315

null,

316

true,

317

loc

318

)

319

}

320

}

321

322

def assertBetween[T: Ordering](value: T, min: T, max: T)(implicit loc: Location): Unit = {

323

val ord = implicitly[Ordering[T]]

324

if (ord.lt(value, min) || ord.gt(value, max)) {

325

throw new FailException(

326

s"Value $value is not between $min and $max",

327

null,

328

true,

329

loc

330

)

331

}

332

}

333

}

334

335

class CustomAssertionExamples extends FunSuite {

336

import CustomAssertions._

337

338

test("email validation") {

339

assertValidEmail("user@example.com")

340

}

341

342

test("range validation") {

343

val score = calculateScore()

344

assertBetween(score, 0.0, 100.0)

345

}

346

}