or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-testing.mdhooks.mdindex.mdmocking.mdrunner.md

hooks.mddocs/

0

# Test Lifecycle Hooks

1

2

Hook functions that run at specific points in the test lifecycle for setup and teardown operations. These hooks provide a clean way to prepare test environments and clean up resources.

3

4

## Capabilities

5

6

### Before Hook

7

8

Runs once before all tests in the current suite. Ideal for expensive setup operations that can be shared across multiple tests.

9

10

```javascript { .api }

11

/**

12

* Runs once before all tests in the current suite

13

* @param fn - Setup function to execute

14

* @param options - Optional hook configuration

15

*/

16

function before(fn: () => void | Promise<void>, options?: { signal?: AbortSignal, timeout?: number }): void;

17

```

18

19

**Usage Examples:**

20

21

```javascript

22

import { describe, it, before } from "test";

23

24

describe("Database tests", () => {

25

before(async () => {

26

// Setup database connection

27

await connectToDatabase();

28

await runMigrations();

29

});

30

31

it("should create user", () => {

32

// Test implementation

33

});

34

35

it("should update user", () => {

36

// Test implementation

37

});

38

});

39

40

// Top-level before hook

41

before(() => {

42

console.log("Starting all tests");

43

process.env.NODE_ENV = "test";

44

});

45

```

46

47

### After Hook

48

49

Runs once after all tests in the current suite have completed. Used for cleanup operations that should happen regardless of test success or failure.

50

51

```javascript { .api }

52

/**

53

* Runs once after all tests in the current suite

54

* @param fn - Cleanup function to execute

55

* @param options - Optional hook configuration

56

*/

57

function after(fn: () => void | Promise<void>, options?: { signal?: AbortSignal, timeout?: number }): void;

58

```

59

60

**Usage Examples:**

61

62

```javascript

63

import { describe, it, before, after } from "test";

64

65

describe("File system tests", () => {

66

before(() => {

67

// Create test directory

68

fs.mkdirSync('./test-files');

69

});

70

71

after(() => {

72

// Clean up test directory

73

fs.rmSync('./test-files', { recursive: true });

74

});

75

76

it("should create file", () => {

77

// Test implementation

78

});

79

});

80

81

// Top-level after hook

82

after(async () => {

83

console.log("All tests completed");

84

await cleanup();

85

});

86

```

87

88

### BeforeEach Hook

89

90

Runs before each individual test. Perfect for ensuring each test starts with a clean, predictable state.

91

92

```javascript { .api }

93

/**

94

* Runs before each individual test

95

* @param fn - Setup function to execute before each test

96

* @param options - Optional hook configuration

97

*/

98

function beforeEach(fn: () => void | Promise<void>, options?: { signal?: AbortSignal, timeout?: number }): void;

99

```

100

101

**Usage Examples:**

102

103

```javascript

104

import { describe, it, beforeEach } from "test";

105

106

describe("Calculator tests", () => {

107

let calculator;

108

109

beforeEach(() => {

110

// Fresh calculator instance for each test

111

calculator = new Calculator();

112

calculator.reset();

113

});

114

115

it("should add numbers", () => {

116

const result = calculator.add(2, 3);

117

if (result !== 5) throw new Error("Addition failed");

118

});

119

120

it("should subtract numbers", () => {

121

const result = calculator.subtract(5, 3);

122

if (result !== 2) throw new Error("Subtraction failed");

123

});

124

});

125

126

// Async beforeEach

127

describe("API tests", () => {

128

beforeEach(async () => {

129

await resetDatabase();

130

await seedTestData();

131

});

132

133

it("should fetch users", async () => {

134

// Test implementation

135

});

136

});

137

```

138

139

### AfterEach Hook

140

141

Runs after each individual test completes. Used for cleaning up test-specific resources and ensuring tests don't interfere with each other.

142

143

```javascript { .api }

144

/**

145

* Runs after each individual test

146

* @param fn - Cleanup function to execute after each test

147

* @param options - Optional hook configuration

148

*/

149

function afterEach(fn: () => void | Promise<void>, options?: { signal?: AbortSignal, timeout?: number }): void;

150

```

151

152

**Usage Examples:**

153

154

```javascript

155

import { describe, it, beforeEach, afterEach } from "test";

156

157

describe("Cache tests", () => {

158

beforeEach(() => {

159

// Initialize cache

160

cache.init();

161

});

162

163

afterEach(() => {

164

// Clear cache after each test

165

cache.clear();

166

});

167

168

it("should store values", () => {

169

cache.set("key", "value");

170

if (cache.get("key") !== "value") {

171

throw new Error("Cache store failed");

172

}

173

});

174

175

it("should handle expiration", async () => {

176

cache.set("key", "value", { ttl: 10 });

177

await new Promise(resolve => setTimeout(resolve, 20));

178

if (cache.get("key") !== null) {

179

throw new Error("Cache expiration failed");

180

}

181

});

182

});

183

184

// Async afterEach

185

describe("Resource tests", () => {

186

afterEach(async () => {

187

await closeConnections();

188

await cleanupTempFiles();

189

});

190

191

it("should manage resources", () => {

192

// Test implementation

193

});

194

});

195

```

196

197

## Hook Execution Order

198

199

Hooks execute in the following order for nested suites:

200

201

1. Outer `before` hooks

202

2. Inner `before` hooks

203

3. For each test:

204

- Outer `beforeEach` hooks

205

- Inner `beforeEach` hooks

206

- **Test execution**

207

- Inner `afterEach` hooks

208

- Outer `afterEach` hooks

209

4. Inner `after` hooks

210

5. Outer `after` hooks

211

212

**Example:**

213

214

```javascript

215

import { describe, it, before, after, beforeEach, afterEach } from "test";

216

217

before(() => console.log("1. Global before"));

218

after(() => console.log("8. Global after"));

219

220

describe("Outer suite", () => {

221

before(() => console.log("2. Outer before"));

222

beforeEach(() => console.log("3. Outer beforeEach"));

223

afterEach(() => console.log("6. Outer afterEach"));

224

after(() => console.log("7. Outer after"));

225

226

describe("Inner suite", () => {

227

before(() => console.log("3. Inner before"));

228

beforeEach(() => console.log("4. Inner beforeEach"));

229

afterEach(() => console.log("5. Inner afterEach"));

230

after(() => console.log("6. Inner after"));

231

232

it("test case", () => {

233

console.log("5. Test execution");

234

});

235

});

236

});

237

```

238

239

## Error Handling in Hooks

240

241

If a hook throws an error or returns a rejected promise:

242

243

- **before/beforeEach errors**: Skip the associated tests

244

- **after/afterEach errors**: Mark tests as failed but continue cleanup

245

- All hooks of the same type continue to run even if one fails

246

247

```javascript

248

describe("Error handling", () => {

249

before(() => {

250

throw new Error("Setup failed");

251

// This will cause all tests in this suite to be skipped

252

});

253

254

afterEach(() => {

255

// This runs even if the test or beforeEach failed

256

cleanup();

257

});

258

259

it("this test will be skipped", () => {

260

// Won't run due to before hook failure

261

});

262

});

263

```

264

265

## Hook Scope

266

267

Hooks only apply to tests within their scope:

268

269

```javascript

270

// Global hooks - apply to all tests

271

before(() => {

272

// Runs before any test

273

});

274

275

describe("Suite A", () => {

276

// Suite-level hooks - only apply to tests in this suite

277

beforeEach(() => {

278

// Only runs before tests in Suite A

279

});

280

281

it("test 1", () => {});

282

it("test 2", () => {});

283

});

284

285

describe("Suite B", () => {

286

// Different suite-level hooks

287

beforeEach(() => {

288

// Only runs before tests in Suite B

289

});

290

291

it("test 3", () => {});

292

});

293

```

294

295

## Best Practices

296

297

1. **Keep hooks simple**: Focus on setup/cleanup, avoid complex logic

298

2. **Use async/await**: For asynchronous operations in hooks

299

3. **Clean up resources**: Always clean up in after/afterEach hooks

300

4. **Fail fast**: If setup fails, let the hook throw an error

301

5. **Scope appropriately**: Use the most specific hook scope possible

302

303

```javascript

304

describe("Best practices example", () => {

305

let server;

306

let client;

307

308

before(async () => {

309

// Expensive setup once per suite

310

server = await startTestServer();

311

});

312

313

beforeEach(() => {

314

// Fresh client for each test

315

client = new ApiClient(server.url);

316

});

317

318

afterEach(() => {

319

// Clean up test-specific resources

320

client.close();

321

});

322

323

after(async () => {

324

// Clean up suite-level resources

325

await server.close();

326

});

327

328

it("should connect", async () => {

329

await client.connect();

330

if (!client.isConnected()) {

331

throw new Error("Connection failed");

332

}

333

});

334

});

335

```