or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdindex.mdlifecycle-hooks.mdmocking.mdsubprocess-testing.mdtest-organization.md

lifecycle-hooks.mddocs/

0

# Lifecycle Hooks

1

2

Before and after hooks for test setup and teardown at multiple levels, providing comprehensive test lifecycle management.

3

4

## Capabilities

5

6

### Test-Level Hooks

7

8

Hooks that run before and after individual tests, useful for test-specific setup and cleanup.

9

10

```typescript { .api }

11

/**

12

* Run a function before the current test starts

13

* @param fn - Function to run before test execution

14

*/

15

function before(fn: () => void | Promise<void>): void;

16

17

/**

18

* Run a function after the current test completes

19

* @param fn - Function to run after test execution

20

*/

21

function after(fn: () => void | Promise<void>): void;

22

```

23

24

**Usage Examples:**

25

26

```typescript

27

import { test, before, after } from "tap";

28

29

test("test with setup and teardown", (t) => {

30

let testData: any;

31

32

before(async () => {

33

// Setup runs before test assertions

34

testData = await setupTestData();

35

console.log("Test setup completed");

36

});

37

38

after(async () => {

39

// Cleanup runs after test completes

40

await cleanupTestData(testData);

41

console.log("Test cleanup completed");

42

});

43

44

t.ok(testData, "test data should be available");

45

t.end();

46

});

47

```

48

49

### Subtest-Level Hooks

50

51

Hooks that run before and after each subtest, perfect for repetitive setup/teardown across multiple related tests.

52

53

```typescript { .api }

54

/**

55

* Run a function before each subtest

56

* @param fn - Function to run before each subtest execution

57

*/

58

function beforeEach(fn: () => void | Promise<void>): void;

59

60

/**

61

* Run a function after each subtest completes

62

* @param fn - Function to run after each subtest execution

63

*/

64

function afterEach(fn: () => void | Promise<void>): void;

65

```

66

67

**Usage Examples:**

68

69

```typescript

70

test("parent test with subtest hooks", (t) => {

71

let sharedResource: any;

72

73

beforeEach(async () => {

74

// Runs before each subtest

75

sharedResource = await createResource();

76

console.log("Subtest setup completed");

77

});

78

79

afterEach(async () => {

80

// Runs after each subtest

81

await cleanupResource(sharedResource);

82

console.log("Subtest cleanup completed");

83

});

84

85

t.test("first subtest", (st) => {

86

st.ok(sharedResource, "resource available");

87

st.end();

88

});

89

90

t.test("second subtest", (st) => {

91

st.ok(sharedResource, "resource available");

92

st.end();

93

});

94

95

t.end();

96

});

97

```

98

99

### Hook Execution Order

100

101

Hooks execute in a predictable order relative to test execution:

102

103

1. `before` hooks (outermost to innermost)

104

2. Test execution begins

105

3. `beforeEach` hooks (for subtests)

106

4. Subtest execution

107

5. `afterEach` hooks (for subtests)

108

6. `after` hooks (innermost to outermost)

109

110

```typescript

111

test("hook execution order", (t) => {

112

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

113

114

beforeEach(() => console.log("2. Parent beforeEach"));

115

116

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

117

118

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

119

120

t.test("subtest", (st) => {

121

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

122

123

after(() => console.log("4. Subtest after"));

124

125

st.ok(true, "subtest assertion");

126

st.end();

127

});

128

129

t.end();

130

});

131

```

132

133

### Async Hook Patterns

134

135

Hooks support both synchronous and asynchronous execution:

136

137

```typescript

138

test("async hooks", (t) => {

139

before(async () => {

140

// Async setup

141

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

142

console.log("Async setup completed");

143

});

144

145

after(() => {

146

// Sync cleanup

147

console.log("Sync cleanup completed");

148

});

149

150

beforeEach(async () => {

151

// Each subtest gets fresh async setup

152

await initializeSubtestData();

153

});

154

155

t.test("async subtest", async (st) => {

156

const result = await performAsyncOperation();

157

st.ok(result.success);

158

});

159

160

t.end();

161

});

162

```

163

164

### Error Handling in Hooks

165

166

Errors in hooks will cause the test to fail:

167

168

```typescript

169

test("hook error handling", (t) => {

170

before(() => {

171

throw new Error("Setup failed");

172

// This will cause the test to fail before any assertions run

173

});

174

175

after(() => {

176

// This still runs even if test or other hooks fail

177

console.log("Cleanup always runs");

178

});

179

180

t.ok(true, "This assertion might not run if before hook fails");

181

t.end();

182

});

183

```

184

185

### Resource Management Patterns

186

187

Common patterns for managing resources with hooks:

188

189

```typescript

190

// Database connection pattern

191

test("database tests", (t) => {

192

let db: Database;

193

194

before(async () => {

195

db = await Database.connect(testConnectionString);

196

});

197

198

after(async () => {

199

if (db) {

200

await db.close();

201

}

202

});

203

204

beforeEach(async () => {

205

// Clean slate for each subtest

206

await db.truncateAll();

207

await db.seedTestData();

208

});

209

210

t.test("user operations", async (st) => {

211

const user = await db.createUser({ name: "test" });

212

st.ok(user.id);

213

});

214

215

t.test("product operations", async (st) => {

216

const product = await db.createProduct({ name: "widget" });

217

st.ok(product.id);

218

});

219

220

t.end();

221

});

222

223

// File system pattern

224

test("file operations", (t) => {

225

let tempDir: string;

226

227

before(async () => {

228

tempDir = await fs.mkdtemp("/tmp/test-");

229

});

230

231

after(async () => {

232

await fs.rmdir(tempDir, { recursive: true });

233

});

234

235

t.test("create file", async (st) => {

236

const filePath = path.join(tempDir, "test.txt");

237

await fs.writeFile(filePath, "test content");

238

st.ok(await fs.pathExists(filePath));

239

});

240

241

t.end();

242

});

243

244

// HTTP server pattern

245

test("API tests", (t) => {

246

let server: Server;

247

let baseUrl: string;

248

249

before(async () => {

250

server = createTestServer();

251

await server.listen(0); // Random port

252

baseUrl = `http://localhost:${server.address().port}`;

253

});

254

255

after(async () => {

256

await server.close();

257

});

258

259

beforeEach(() => {

260

// Reset server state between tests

261

server.resetMocks();

262

});

263

264

t.test("GET /users", async (st) => {

265

const response = await fetch(`${baseUrl}/users`);

266

st.equal(response.status, 200);

267

});

268

269

t.end();

270

});

271

```

272

273

### Hook Scope and Inheritance

274

275

Hooks are scoped to their containing test and inherited by subtests:

276

277

```typescript

278

test("parent", (t) => {

279

before(() => console.log("Parent before"));

280

beforeEach(() => console.log("Parent beforeEach"));

281

282

t.test("child", (st) => {

283

// Inherits parent hooks

284

before(() => console.log("Child before"));

285

286

st.test("grandchild", (gst) => {

287

// Inherits both parent and child hooks

288

gst.ok(true);

289

gst.end();

290

});

291

292

st.end();

293

});

294

295

t.end();

296

});

297

```

298

299

### Multiple Hooks

300

301

You can register multiple hooks of the same type:

302

303

```typescript

304

test("multiple hooks", (t) => {

305

before(() => console.log("First before"));

306

before(() => console.log("Second before"));

307

308

after(() => console.log("First after"));

309

after(() => console.log("Second after"));

310

311

// All hooks run in registration order

312

t.ok(true);

313

t.end();

314

});

315

```

316

317

### Conditional Hooks

318

319

Hooks can be conditionally registered:

320

321

```typescript

322

test("conditional setup", (t) => {

323

if (process.env.ENABLE_SLOW_SETUP) {

324

before(async () => {

325

await performExpensiveSetup();

326

});

327

}

328

329

if (needsCleanup) {

330

after(() => {

331

performCleanup();

332

});

333

}

334

335

t.ok(true);

336

t.end();

337

});

338

```