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

mocking.mddocs/

0

# Mocking and Interception

1

2

Module mocking, function interception, and call capturing for isolating tests and controlling dependencies.

3

4

## Capabilities

5

6

### Module Mocking

7

8

Mock Node.js modules to control dependencies and isolate units under test.

9

10

```typescript { .api }

11

/**

12

* Mock a CommonJS module with custom implementations

13

* @param module - Module name or path to mock

14

* @param mocks - Object containing mock implementations

15

*/

16

function mockRequire(module: string, mocks?: any): void;

17

18

/**

19

* Mock an ES module with custom implementations

20

* @param module - Module name or path to mock

21

* @param mocks - Object containing mock implementations

22

* @returns Promise that resolves when mock is registered

23

*/

24

function mockImport(module: string, mocks?: any): Promise<void>;

25

26

/**

27

* Create a mock object with base properties and overrides

28

* @param base - Base object to extend (optional)

29

* @param overrides - Properties to override or add

30

* @returns Mock object with specified properties

31

*/

32

function createMock(base?: any, overrides?: any): any;

33

```

34

35

**Usage Examples:**

36

37

```typescript

38

import { test, mockRequire, mockImport } from "tap";

39

40

// Mock CommonJS module

41

test("mock CommonJS fs module", (t) => {

42

mockRequire("fs", {

43

readFileSync: () => "mocked file content",

44

writeFileSync: () => undefined,

45

existsSync: () => true

46

});

47

48

const fs = require("fs");

49

const content = fs.readFileSync("any-file.txt");

50

t.equal(content, "mocked file content");

51

t.end();

52

});

53

54

// Mock ESM module

55

test("mock ES module", async (t) => {

56

await mockImport("node:fs/promises", {

57

readFile: async () => "mocked async content",

58

writeFile: async () => undefined

59

});

60

61

const { readFile } = await import("node:fs/promises");

62

const content = await readFile("any-file.txt");

63

t.equal(content, "mocked async content");

64

});

65

66

// Create mock objects

67

test("create mock objects", (t) => {

68

const mockUser = createMock({ id: 1 }, {

69

name: "Test User",

70

save: () => Promise.resolve()

71

});

72

73

t.equal(mockUser.id, 1);

74

t.equal(mockUser.name, "Test User");

75

t.type(mockUser.save, "function");

76

t.end();

77

});

78

```

79

80

### Function Interception

81

82

Intercept and monitor function calls for testing behavior and side effects.

83

84

```typescript { .api }

85

/**

86

* Intercept property access on an object

87

* @param object - Target object to intercept

88

* @param property - Property name to intercept

89

* @param options - Interception configuration options

90

*/

91

function intercept(object: any, property: string, options?: InterceptOpts): void;

92

93

/**

94

* Capture function calls with detailed information

95

* @param fn - Function to capture calls for

96

* @returns Captured function with call information

97

*/

98

function captureFn(fn: Function): CapturedFunction;

99

100

/**

101

* Capture method calls on an object

102

* @param object - Target object

103

* @param property - Method name to capture

104

* @param fn - Optional replacement function

105

* @returns Original method for restoration

106

*/

107

function capture(object: any, property: string, fn?: Function): Function;

108

```

109

110

**Usage Examples:**

111

112

```typescript

113

// Intercept console.log calls

114

test("intercept console output", (t) => {

115

const messages: string[] = [];

116

117

intercept(console, "log", {

118

value: (message: string) => {

119

messages.push(message);

120

}

121

});

122

123

console.log("Hello");

124

console.log("World");

125

126

t.same(messages, ["Hello", "World"]);

127

t.end();

128

});

129

130

// Capture function calls

131

test("capture function calls", (t) => {

132

const originalFn = (x: number, y: number) => x + y;

133

const captured = captureFn(originalFn);

134

135

const result1 = captured(2, 3);

136

const result2 = captured(5, 7);

137

138

t.equal(result1, 5);

139

t.equal(result2, 12);

140

t.equal(captured.calls.length, 2);

141

t.same(captured.calls[0].args, [2, 3]);

142

t.same(captured.calls[1].args, [5, 7]);

143

t.end();

144

});

145

146

// Capture method calls

147

test("capture method calls", (t) => {

148

const api = {

149

save: (data: any) => Promise.resolve({ id: 1, ...data }),

150

delete: (id: number) => Promise.resolve()

151

};

152

153

const originalSave = capture(api, "save");

154

155

api.save({ name: "test" });

156

api.save({ name: "test2" });

157

158

// Access captured calls

159

t.equal(api.save.calls.length, 2);

160

t.same(api.save.calls[0].args, [{ name: "test" }]);

161

162

// Restore original

163

api.save = originalSave;

164

t.end();

165

});

166

```

167

168

### Advanced Mocking Patterns

169

170

More sophisticated mocking scenarios for complex testing needs.

171

172

```typescript

173

// Mock with dynamic behavior

174

test("dynamic mock behavior", (t) => {

175

let callCount = 0;

176

177

mockRequire("axios", {

178

get: (url: string) => {

179

callCount++;

180

if (callCount === 1) {

181

return Promise.resolve({ data: "first call" });

182

} else {

183

return Promise.reject(new Error("rate limited"));

184

}

185

}

186

});

187

188

const axios = require("axios");

189

190

axios.get("http://api.example.com")

191

.then((response: any) => {

192

t.equal(response.data, "first call");

193

194

return axios.get("http://api.example.com");

195

})

196

.catch((error: Error) => {

197

t.match(error.message, /rate limited/);

198

t.end();

199

});

200

});

201

202

// Mock with state tracking

203

test("stateful mocks", (t) => {

204

const mockDatabase = {

205

data: new Map(),

206

207

save: function(id: string, value: any) {

208

this.data.set(id, value);

209

return Promise.resolve(value);

210

},

211

212

find: function(id: string) {

213

return Promise.resolve(this.data.get(id));

214

},

215

216

clear: function() {

217

this.data.clear();

218

}

219

};

220

221

mockRequire("./database", mockDatabase);

222

223

const db = require("./database");

224

225

db.save("user1", { name: "Alice" })

226

.then(() => db.find("user1"))

227

.then((user: any) => {

228

t.same(user, { name: "Alice" });

229

t.end();

230

});

231

});

232

```

233

234

### Partial Mocking

235

236

Mock only specific parts of modules while preserving other functionality.

237

238

```typescript

239

test("partial module mocking", async (t) => {

240

// Only mock specific methods of fs module

241

const originalFs = await import("node:fs/promises");

242

243

await mockImport("node:fs/promises", {

244

...originalFs,

245

readFile: async () => "mocked content",

246

// Other methods remain unchanged

247

});

248

249

const fs = await import("node:fs/promises");

250

const content = await fs.readFile("test.txt");

251

252

t.equal(content, "mocked content");

253

// fs.writeFile, fs.stat, etc. still work normally

254

});

255

```

256

257

### Mock Cleanup and Restoration

258

259

Properly clean up mocks to avoid test interference.

260

261

```typescript

262

test("mock cleanup", (t) => {

263

// Store original for restoration

264

const originalLog = console.log;

265

let logMessages: string[] = [];

266

267

intercept(console, "log", {

268

value: (message: string) => {

269

logMessages.push(message);

270

}

271

});

272

273

console.log("test message");

274

275

t.same(logMessages, ["test message"]);

276

277

// Restore original (TAP usually handles this automatically)

278

console.log = originalLog;

279

280

t.end();

281

});

282

```

283

284

### Types

285

286

```typescript { .api }

287

interface InterceptOpts {

288

/** Replacement value or function */

289

value?: any;

290

291

/** Whether to call original after intercept */

292

callOriginal?: boolean;

293

294

/** Function to call before original */

295

before?: Function;

296

297

/** Function to call after original */

298

after?: Function;

299

}

300

301

interface CapturedFunction extends Function {

302

/** Array of captured call information */

303

calls: CallInfo[];

304

305

/** Original function that was captured */

306

original: Function;

307

308

/** Reset captured calls */

309

reset(): void;

310

}

311

312

interface CallInfo {

313

/** Arguments passed to the function */

314

args: any[];

315

316

/** Return value of the function call */

317

result?: any;

318

319

/** Error thrown by the function (if any) */

320

error?: Error;

321

322

/** Timestamp of the call */

323

timestamp: number;

324

325

/** Context (this value) of the call */

326

context?: any;

327

}

328

```

329

330

### Testing with External Dependencies

331

332

Common patterns for mocking external services and dependencies.

333

334

```typescript

335

// Mock HTTP client

336

test("HTTP service testing", async (t) => {

337

await mockImport("node:http", {

338

request: (options: any, callback: Function) => {

339

const mockResponse = {

340

statusCode: 200,

341

headers: { "content-type": "application/json" },

342

on: (event: string, handler: Function) => {

343

if (event === "data") {

344

handler('{"success": true}');

345

}

346

if (event === "end") {

347

handler();

348

}

349

}

350

};

351

callback(mockResponse);

352

return { end: () => {}, on: () => {} };

353

}

354

});

355

356

// Your HTTP service code here

357

// const result = await httpService.get("http://api.example.com");

358

// t.ok(result.success);

359

});

360

361

// Mock environment variables

362

test("environment-dependent code", (t) => {

363

const originalEnv = process.env.NODE_ENV;

364

process.env.NODE_ENV = "test";

365

366

// Test environment-specific behavior

367

t.equal(getEnvironment(), "test");

368

369

// Restore

370

process.env.NODE_ENV = originalEnv;

371

t.end();

372

});

373

374

// Mock timers

375

test("time-dependent code", (t) => {

376

const originalSetTimeout = global.setTimeout;

377

let timeoutCallback: Function;

378

379

global.setTimeout = (callback: Function, delay: number) => {

380

timeoutCallback = callback;

381

return 1; // Mock timer ID

382

};

383

384

startTimedProcess();

385

386

// Manually trigger timeout

387

timeoutCallback();

388

389

t.ok(isProcessComplete());

390

391

// Restore

392

global.setTimeout = originalSetTimeout;

393

t.end();

394

});

395

```