or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

array-assertions.mdcollection-assertions.mdcore-assertions.mdcustom-assertions.mdexception-assertions.mdindex.mdjava8-assertions.mdmap-assertions.mdnumeric-assertions.mdstring-assertions.mdtesting-utilities.md

exception-assertions.mddocs/

0

# Exception Assertions

1

2

Specialized assertions for throwables and exceptions including message and cause chain validation.

3

4

## Capabilities

5

6

### Throwable Subject Creation

7

8

Entry point for creating assertions about exceptions and throwables.

9

10

```java { .api }

11

/**

12

* Creates a ThrowableSubject for asserting about Throwable instances.

13

* @param actual the Throwable under test

14

*/

15

public static ThrowableSubject assertThat(Throwable actual);

16

```

17

18

**Usage Examples:**

19

20

```java

21

try {

22

someMethodThatThrows();

23

fail("Expected exception to be thrown");

24

} catch (Exception e) {

25

assertThat(e).isInstanceOf(IllegalArgumentException.class);

26

}

27

```

28

29

### Exception Message Assertions

30

31

Methods for asserting about exception messages using StringSubject capabilities.

32

33

```java { .api }

34

/**

35

* Returns a StringSubject for making assertions about the exception's message.

36

* The message is obtained via getMessage().

37

*/

38

public StringSubject hasMessageThat();

39

```

40

41

**Usage Examples:**

42

43

```java

44

IllegalArgumentException exception =

45

new IllegalArgumentException("Invalid input: null value not allowed");

46

47

// Basic message assertions

48

assertThat(exception).hasMessageThat().contains("Invalid input");

49

assertThat(exception).hasMessageThat().startsWith("Invalid");

50

assertThat(exception).hasMessageThat().endsWith("not allowed");

51

52

// Pattern matching in messages

53

assertThat(exception).hasMessageThat().matches("Invalid input: .* not allowed");

54

assertThat(exception).hasMessageThat().containsMatch("null value");

55

56

// Case-insensitive message checks

57

assertThat(exception).hasMessageThat().ignoringCase().contains("INVALID");

58

```

59

60

### Exception Cause Assertions

61

62

Methods for asserting about exception causes, enabling deep cause chain validation.

63

64

```java { .api }

65

/**

66

* Returns a ThrowableSubject for making assertions about the exception's cause.

67

* The cause is obtained via getCause().

68

*/

69

public ThrowableSubject hasCauseThat();

70

```

71

72

**Usage Examples:**

73

74

```java

75

// Exception with nested causes

76

IOException ioException = new IOException("File not found");

77

RuntimeException runtimeException = new RuntimeException("Processing failed", ioException);

78

79

// Asserting about direct cause

80

assertThat(runtimeException).hasCauseThat().isInstanceOf(IOException.class);

81

assertThat(runtimeException).hasCauseThat().hasMessageThat().contains("File not found");

82

83

// Chaining cause assertions for deep cause chains

84

DatabaseException dbException = new DatabaseException("Connection failed");

85

ServiceException serviceException = new ServiceException("Service unavailable", dbException);

86

ApplicationException appException = new ApplicationException("Request failed", serviceException);

87

88

assertThat(appException)

89

.hasCauseThat().isInstanceOf(ServiceException.class)

90

.hasCauseThat().isInstanceOf(DatabaseException.class)

91

.hasMessageThat().contains("Connection failed");

92

```

93

94

### Common Exception Testing Patterns

95

96

Real-world patterns for testing exception behavior in applications.

97

98

#### Testing Expected Exceptions

99

100

```java

101

// Method that should throw exception

102

@Test

103

public void testInvalidInput_ThrowsException() {

104

IllegalArgumentException exception = assertThrows(

105

IllegalArgumentException.class,

106

() -> calculator.divide(10, 0)

107

);

108

109

assertThat(exception).hasMessageThat().contains("Division by zero");

110

}

111

112

// Using Truth with assertThrows

113

@Test

114

public void testFileOperation_ThrowsIOException() {

115

IOException exception = assertThrows(

116

IOException.class,

117

() -> fileService.readNonExistentFile("missing.txt")

118

);

119

120

assertThat(exception).hasMessageThat().startsWith("File not found");

121

assertThat(exception).hasCauseThat().isNull();

122

}

123

```

124

125

#### Validation Exception Testing

126

127

```java

128

// Testing validation exceptions with detailed messages

129

@Test

130

public void testUserValidation_InvalidEmail() {

131

ValidationException exception = assertThrows(

132

ValidationException.class,

133

() -> userService.createUser("invalid-email", "password")

134

);

135

136

assertThat(exception).hasMessageThat().contains("email");

137

assertThat(exception).hasMessageThat().matches(".*email.*format.*");

138

assertThat(exception).hasMessageThat().ignoringCase().contains("INVALID");

139

}

140

141

// Testing multiple validation errors

142

@Test

143

public void testUserValidation_MultipleErrors() {

144

ValidationException exception = assertThrows(

145

ValidationException.class,

146

() -> userService.createUser("", "")

147

);

148

149

assertThat(exception).hasMessageThat().contains("email");

150

assertThat(exception).hasMessageThat().contains("password");

151

}

152

```

153

154

#### Exception Chaining Validation

155

156

```java

157

// Testing proper exception wrapping in service layers

158

@Test

159

public void testServiceLayer_WrapsDataAccessExceptions() {

160

// Simulate data access exception

161

when(userRepository.findById(1L)).thenThrow(

162

new DataAccessException("Database connection timeout")

163

);

164

165

ServiceException exception = assertThrows(

166

ServiceException.class,

167

() -> userService.getUser(1L)

168

);

169

170

// Verify exception wrapping

171

assertThat(exception).hasMessageThat().contains("Failed to retrieve user");

172

assertThat(exception).hasCauseThat().isInstanceOf(DataAccessException.class);

173

assertThat(exception).hasCauseThat().hasMessageThat().contains("Database connection timeout");

174

}

175

```

176

177

#### Security Exception Testing

178

179

```java

180

// Testing authentication exceptions

181

@Test

182

public void testAuthentication_InvalidCredentials() {

183

AuthenticationException exception = assertThrows(

184

AuthenticationException.class,

185

() -> authService.authenticate("user", "wrongpassword")

186

);

187

188

assertThat(exception).hasMessageThat().contains("Invalid credentials");

189

assertThat(exception).hasMessageThat().doesNotContain("password"); // Security: don't leak password details

190

}

191

192

// Testing authorization exceptions

193

@Test

194

public void testAuthorization_InsufficientPermissions() {

195

AuthorizationException exception = assertThrows(

196

AuthorizationException.class,

197

() -> adminService.deleteUser(regularUser, targetUserId)

198

);

199

200

assertThat(exception).hasMessageThat().contains("Insufficient permissions");

201

assertThat(exception).hasMessageThat().containsMatch("user.*not authorized");

202

}

203

```

204

205

#### Configuration Exception Testing

206

207

```java

208

// Testing configuration validation

209

@Test

210

public void testConfiguration_MissingProperty() {

211

ConfigurationException exception = assertThrows(

212

ConfigurationException.class,

213

() -> configService.loadConfig("invalid-config.properties")

214

);

215

216

assertThat(exception).hasMessageThat().contains("Missing required property");

217

assertThat(exception).hasMessageThat().matches(".*database\\.url.*required.*");

218

}

219

220

// Testing configuration format errors

221

@Test

222

public void testConfiguration_InvalidFormat() {

223

ConfigurationException exception = assertThrows(

224

ConfigurationException.class,

225

() -> configService.loadConfig("malformed-config.xml")

226

);

227

228

assertThat(exception).hasMessageThat().contains("Invalid XML format");

229

assertThat(exception).hasCauseThat().isInstanceOf(XMLParseException.class);

230

}

231

```

232

233

#### Timeout and Resource Exception Testing

234

235

```java

236

// Testing timeout exceptions

237

@Test

238

public void testNetworkCall_Timeout() {

239

TimeoutException exception = assertThrows(

240

TimeoutException.class,

241

() -> networkService.callExternalApi()

242

);

243

244

assertThat(exception).hasMessageThat().contains("timeout");

245

assertThat(exception).hasMessageThat().containsMatch("\\d+ms"); // Contains timeout duration

246

}

247

248

// Testing resource exhaustion

249

@Test

250

public void testResourceExhaustion_OutOfMemory() {

251

ResourceException exception = assertThrows(

252

ResourceException.class,

253

() -> memoryIntensiveOperation()

254

);

255

256

assertThat(exception).hasMessageThat().contains("Out of memory");

257

assertThat(exception).hasCauseThat().isInstanceOf(OutOfMemoryError.class);

258

}

259

```

260

261

#### Custom Exception Testing

262

263

```java

264

// Testing custom business exceptions

265

@Test

266

public void testBusinessLogic_InvalidBusinessRule() {

267

BusinessRuleException exception = assertThrows(

268

BusinessRuleException.class,

269

() -> orderService.processOrder(invalidOrder)

270

);

271

272

assertThat(exception).hasMessageThat().contains("Business rule violation");

273

assertThat(exception).hasMessageThat().contains(invalidOrder.getId().toString());

274

assertThat(exception.getErrorCode()).isEqualTo("INVALID_ORDER_STATE");

275

}

276

277

// Testing exception with custom properties

278

public class CustomException extends Exception {

279

private final String errorCode;

280

private final int statusCode;

281

282

// constructor and getters...

283

}

284

285

@Test

286

public void testCustomException_Properties() {

287

CustomException exception = assertThrows(

288

CustomException.class,

289

() -> customService.performOperation()

290

);

291

292

assertThat(exception).hasMessageThat().contains("Operation failed");

293

assertThat(exception.getErrorCode()).isEqualTo("CUSTOM_ERROR");

294

assertThat(exception.getStatusCode()).isEqualTo(500);

295

}

296

```

297

298

#### Exception Stack Trace Validation

299

300

```java

301

// Testing that exception originates from expected location

302

@Test

303

public void testException_OriginatesFromCorrectLocation() {

304

RuntimeException exception = assertThrows(

305

RuntimeException.class,

306

() -> serviceMethodThatShouldFail()

307

);

308

309

assertThat(exception).hasMessageThat().isNotNull();

310

311

// Verify stack trace contains expected method

312

StackTraceElement[] stackTrace = exception.getStackTrace();

313

assertThat(stackTrace).isNotEmpty();

314

315

boolean foundExpectedMethod = Arrays.stream(stackTrace)

316

.anyMatch(element -> element.getMethodName().equals("serviceMethodThatShouldFail"));

317

assertThat(foundExpectedMethod).isTrue();

318

}

319

```

320

321

### Exception Testing Best Practices

322

323

#### Comprehensive Exception Assertion Pattern

324

325

```java

326

/**

327

* Comprehensive exception testing helper method

328

*/

329

public static void assertExceptionDetails(

330

Throwable exception,

331

Class<? extends Throwable> expectedType,

332

String expectedMessageSubstring,

333

Class<? extends Throwable> expectedCauseType) {

334

335

assertThat(exception).isInstanceOf(expectedType);

336

assertThat(exception).hasMessageThat().contains(expectedMessageSubstring);

337

338

if (expectedCauseType != null) {

339

assertThat(exception).hasCauseThat().isInstanceOf(expectedCauseType);

340

}

341

}

342

343

// Usage

344

@Test

345

public void testComplexOperation_ProperExceptionHandling() {

346

ServiceException exception = assertThrows(

347

ServiceException.class,

348

() -> complexService.performComplexOperation()

349

);

350

351

assertExceptionDetails(

352

exception,

353

ServiceException.class,

354

"Complex operation failed",

355

DataAccessException.class

356

);

357

}

358

```

359

360

## Types

361

362

```java { .api }

363

/**

364

* Subject class for making assertions about Throwable instances.

365

*/

366

public class ThrowableSubject extends Subject {

367

/**

368

* Constructor for ThrowableSubject.

369

* @param metadata failure metadata for context

370

* @param actual the Throwable under test

371

*/

372

protected ThrowableSubject(FailureMetadata metadata, Throwable actual);

373

374

/**

375

* Returns a StringSubject for making assertions about the exception's message.

376

* The message is obtained via getMessage().

377

*/

378

public StringSubject hasMessageThat();

379

380

/**

381

* Returns a ThrowableSubject for making assertions about the exception's cause.

382

* The cause is obtained via getCause().

383

*/

384

public ThrowableSubject hasCauseThat();

385

}

386

```