or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asymmetric-matchers.mdcollection-string-matchers.mdconfiguration-extension.mdcore-matchers.mdexception-matchers.mdindex.mdnumeric-matchers.mdspy-mock-matchers.md
tile.json

exception-matchers.mddocs/

0

# Exception Matchers

1

2

Matchers for testing error conditions and exception handling. These matchers are essential for verifying that functions throw appropriate errors and for testing error handling logic.

3

4

## Capabilities

5

6

### Basic Exception Testing

7

8

#### toThrow

9

10

Tests that a function throws an error when called. Can optionally verify the error message, error type, or error pattern.

11

12

```javascript { .api }

13

/**

14

* Checks if function throws an error, optionally matching type, message, or pattern

15

* @param expected - Optional: Error constructor, error message string, or RegExp pattern

16

*/

17

ExpectationObject.toThrow(expected?: string | Error | RegExp): void;

18

```

19

20

**Usage Examples:**

21

22

```javascript

23

// Test that a function throws any error

24

expect(() => {

25

throw new Error('Something went wrong');

26

}).toThrow();

27

28

// Test that a function throws with specific message

29

expect(() => {

30

throw new Error('Invalid input');

31

}).toThrow('Invalid input');

32

33

// Test with partial message matching

34

expect(() => {

35

throw new Error('Database connection failed: timeout');

36

}).toThrow('Database connection failed');

37

38

// Test with regex pattern

39

expect(() => {

40

throw new Error('User not found: ID 123');

41

}).toThrow(/User not found: ID \d+/);

42

43

// Test with Error constructor

44

expect(() => {

45

throw new TypeError('Expected string');

46

}).toThrow(TypeError);

47

48

// Test with specific Error instance

49

expect(() => {

50

throw new RangeError('Value out of range');

51

}).toThrow(new RangeError('Value out of range'));

52

```

53

54

#### toThrowError

55

56

Alias for `toThrow` - functions identically.

57

58

```javascript { .api }

59

/**

60

* Alias for toThrow - checks if function throws error

61

* @param expected - Optional: Error constructor, error message string, or RegExp pattern

62

*/

63

ExpectationObject.toThrowError(expected?: string | Error | RegExp): void;

64

```

65

66

**Usage Examples:**

67

68

```javascript

69

// All examples from toThrow work identically with toThrowError

70

expect(() => {

71

throw new Error('Something went wrong');

72

}).toThrowError();

73

74

expect(() => {

75

throw new Error('Invalid input');

76

}).toThrowError('Invalid input');

77

78

expect(() => {

79

throw new TypeError('Expected string');

80

}).toThrowError(TypeError);

81

```

82

83

## Advanced Usage Patterns

84

85

### Testing Custom Error Classes

86

87

```javascript

88

class ValidationError extends Error {

89

constructor(field, message) {

90

super(`Validation failed for ${field}: ${message}`);

91

this.name = 'ValidationError';

92

this.field = field;

93

}

94

}

95

96

class NetworkError extends Error {

97

constructor(statusCode, message) {

98

super(message);

99

this.name = 'NetworkError';

100

this.statusCode = statusCode;

101

}

102

}

103

104

function validateUser(userData) {

105

if (!userData.email) {

106

throw new ValidationError('email', 'Email is required');

107

}

108

if (!userData.email.includes('@')) {

109

throw new ValidationError('email', 'Invalid email format');

110

}

111

}

112

113

// Test specific error types

114

expect(() => validateUser({})).toThrow(ValidationError);

115

116

// Test error messages

117

expect(() => validateUser({})).toThrow('Validation failed for email: Email is required');

118

119

// Test with regex for flexible matching

120

expect(() => validateUser({email: 'invalid'})).toThrow(/Validation failed for email:/);

121

122

// Test that valid data doesn't throw

123

expect(() => validateUser({email: 'user@example.com'})).not.toThrow();

124

```

125

126

### Testing Async Functions

127

128

```javascript

129

// Testing async functions that throw

130

async function fetchUserData(userId) {

131

if (!userId) {

132

throw new Error('User ID is required');

133

}

134

if (userId < 0) {

135

throw new RangeError('User ID must be positive');

136

}

137

// fetch implementation...

138

}

139

140

// Use await expect for async functions

141

await expect(async () => {

142

await fetchUserData();

143

}).rejects.toThrow('User ID is required');

144

145

await expect(async () => {

146

await fetchUserData(-1);

147

}).rejects.toThrow(RangeError);

148

149

// Alternative syntax

150

expect(fetchUserData()).rejects.toThrow('User ID is required');

151

expect(fetchUserData(-1)).rejects.toThrow(RangeError);

152

```

153

154

### Testing Promise Rejections

155

156

```javascript

157

function processData(data) {

158

return new Promise((resolve, reject) => {

159

if (!data) {

160

reject(new Error('No data provided'));

161

} else if (data.invalid) {

162

reject(new ValidationError('data', 'Invalid data structure'));

163

} else {

164

resolve(data);

165

}

166

});

167

}

168

169

// Test promise rejections

170

await expect(processData()).rejects.toThrow('No data provided');

171

await expect(processData({invalid: true})).rejects.toThrow(ValidationError);

172

173

// Test successful resolution

174

await expect(processData({valid: true})).resolves.toEqual({valid: true});

175

```

176

177

### Testing Error Handling in Complex Scenarios

178

179

```javascript

180

class ApiClient {

181

async request(endpoint, options = {}) {

182

if (!endpoint) {

183

throw new Error('Endpoint is required');

184

}

185

186

if (options.timeout && options.timeout < 0) {

187

throw new RangeError('Timeout must be positive');

188

}

189

190

// Simulate network error

191

if (endpoint.includes('unavailable')) {

192

throw new NetworkError(503, 'Service unavailable');

193

}

194

195

return {data: 'success'};

196

}

197

}

198

199

const client = new ApiClient();

200

201

// Test various error conditions

202

await expect(() => client.request()).rejects.toThrow('Endpoint is required');

203

204

await expect(() =>

205

client.request('/api/data', {timeout: -1})

206

).rejects.toThrow(RangeError);

207

208

await expect(() =>

209

client.request('/api/unavailable')

210

).rejects.toThrow(NetworkError);

211

212

await expect(() =>

213

client.request('/api/unavailable')

214

).rejects.toThrow('Service unavailable');

215

216

// Test successful case

217

await expect(client.request('/api/data')).resolves.toEqual({data: 'success'});

218

```

219

220

### Testing Error Boundaries and Try-Catch Blocks

221

222

```javascript

223

function safeParseJSON(jsonString) {

224

try {

225

return JSON.parse(jsonString);

226

} catch (error) {

227

throw new Error(`Invalid JSON: ${error.message}`);

228

}

229

}

230

231

function unsafeParseJSON(jsonString) {

232

return JSON.parse(jsonString); // Let SyntaxError bubble up

233

}

234

235

// Test wrapped error handling

236

expect(() => safeParseJSON('{')).toThrow('Invalid JSON:');

237

expect(() => safeParseJSON('{')).toThrow(/Invalid JSON:/);

238

239

// Test raw error bubbling

240

expect(() => unsafeParseJSON('{')).toThrow(SyntaxError);

241

expect(() => unsafeParseJSON('{')).toThrow('Unexpected end of JSON input');

242

243

// Test successful parsing

244

expect(() => safeParseJSON('{"valid": true}')).not.toThrow();

245

expect(safeParseJSON('{"valid": true}')).toEqual({valid: true});

246

```

247

248

### Testing Multiple Error Conditions

249

250

```javascript

251

function divide(a, b) {

252

if (typeof a !== 'number' || typeof b !== 'number') {

253

throw new TypeError('Both arguments must be numbers');

254

}

255

if (b === 0) {

256

throw new Error('Division by zero');

257

}

258

if (!Number.isFinite(a) || !Number.isFinite(b)) {

259

throw new RangeError('Arguments must be finite numbers');

260

}

261

return a / b;

262

}

263

264

// Test type errors

265

expect(() => divide('5', 2)).toThrow(TypeError);

266

expect(() => divide(5, '2')).toThrow('Both arguments must be numbers');

267

268

// Test division by zero

269

expect(() => divide(10, 0)).toThrow('Division by zero');

270

271

// Test infinite numbers

272

expect(() => divide(Infinity, 5)).toThrow(RangeError);

273

expect(() => divide(5, NaN)).toThrow('Arguments must be finite numbers');

274

275

// Test successful operation

276

expect(() => divide(10, 2)).not.toThrow();

277

expect(divide(10, 2)).toBe(5);

278

```

279

280

## Testing Error Messages with Internationalization

281

282

```javascript

283

const ERROR_MESSAGES = {

284

en: {

285

REQUIRED_FIELD: 'This field is required',

286

INVALID_EMAIL: 'Please enter a valid email address'

287

},

288

es: {

289

REQUIRED_FIELD: 'Este campo es obligatorio',

290

INVALID_EMAIL: 'Por favor ingrese una dirección de correo válida'

291

}

292

};

293

294

function validateWithLocale(value, locale = 'en') {

295

if (!value) {

296

throw new Error(ERROR_MESSAGES[locale].REQUIRED_FIELD);

297

}

298

}

299

300

// Test different locales

301

expect(() => validateWithLocale('', 'en')).toThrow('This field is required');

302

expect(() => validateWithLocale('', 'es')).toThrow('Este campo es obligatorio');

303

304

// Test with regex for flexible matching

305

expect(() => validateWithLocale('', 'en')).toThrow(/required/i);

306

expect(() => validateWithLocale('', 'es')).toThrow(/obligatorio/i);

307

```

308

309

## Negation Support

310

311

Exception matchers support negation to test that functions do NOT throw:

312

313

```javascript

314

function safeOperation(value) {

315

if (value > 0) {

316

return value * 2;

317

}

318

return 0;

319

}

320

321

expect(() => safeOperation(5)).not.toThrow();

322

expect(() => safeOperation(0)).not.toThrow();

323

expect(() => safeOperation(-1)).not.toThrow();

324

325

// Test that async functions don't reject

326

await expect(Promise.resolve('success')).resolves.not.toThrow();

327

```

328

329

## Promise Support

330

331

Exception matchers work seamlessly with promise-based testing:

332

333

```javascript

334

// Test promise rejections

335

await expect(Promise.reject(new Error('Failed'))).rejects.toThrow('Failed');

336

await expect(Promise.reject(new TypeError('Type error'))).rejects.toThrow(TypeError);

337

338

// Test promise resolutions (should not throw)

339

await expect(Promise.resolve('success')).resolves.not.toThrow();

340

```