or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mddom-testing.mdindex.mdmocking.md

mocking.mddocs/

0

# Mocking and Spying

1

2

Mock functions and spies with reactive instrumentation for Storybook integration. All mock functions are automatically tracked and can be debugged in the addon-interactions panel, with automatic cleanup between stories.

3

4

## Capabilities

5

6

### Mock Functions

7

8

Create mock functions that track calls and can be configured with custom implementations.

9

10

```typescript { .api }

11

/**

12

* Create a mock function with optional implementation

13

* @param implementation - Optional function implementation

14

* @returns Mock function with tracking capabilities

15

*/

16

function fn<T extends Procedure = Procedure>(implementation?: T): Mock<T>;

17

18

// Legacy overloads (deprecated in v9.0)

19

function fn<TArgs extends any[] = any, R = any>(): Mock<(...args: TArgs) => R>;

20

function fn<TArgs extends any[] = any[], R = any>(

21

implementation: (...args: TArgs) => R

22

): Mock<(...args: TArgs) => R>;

23

24

type Procedure = (...args: any[]) => any;

25

26

type Mock<T extends Procedure | any[] = any[], R = any> = T extends Procedure

27

? MockV2<T>

28

: T extends any[]

29

? MockV2<(...args: T) => R>

30

: never;

31

```

32

33

**Usage Examples:**

34

35

```typescript

36

import { fn, expect } from '@storybook/test';

37

38

// Create a basic mock

39

const mockCallback = fn();

40

mockCallback('arg1', 'arg2');

41

expect(mockCallback).toHaveBeenCalledWith('arg1', 'arg2');

42

43

// Create a mock with implementation

44

const mockAdd = fn((a: number, b: number) => a + b);

45

const result = mockAdd(2, 3);

46

expect(result).toBe(5);

47

expect(mockAdd).toHaveBeenCalledWith(2, 3);

48

49

// Mock with custom name for debugging

50

const namedMock = fn().mockName('customName');

51

expect(namedMock.getMockName()).toBe('customName');

52

```

53

54

### Spying on Objects

55

56

Create spies on existing object methods while preserving original behavior.

57

58

```typescript { .api }

59

/**

60

* Create a spy on an object method

61

* @param object - Target object

62

* @param method - Method name to spy on

63

* @returns Mock instance that wraps the original method

64

*/

65

function spyOn<T, K extends keyof T>(

66

object: T,

67

method: K

68

): T[K] extends (...args: any[]) => any ? MockInstance<Parameters<T[K]>, ReturnType<T[K]>> : never;

69

```

70

71

**Usage Examples:**

72

73

```typescript

74

import { spyOn, expect } from '@storybook/test';

75

76

class Calculator {

77

add(a: number, b: number) {

78

return a + b;

79

}

80

}

81

82

const calc = new Calculator();

83

84

export const SpyStory = {

85

play: async () => {

86

// Spy on the method while preserving original behavior

87

const addSpy = spyOn(calc, 'add');

88

89

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

90

expect(result).toBe(5); // Original behavior preserved

91

expect(addSpy).toHaveBeenCalledWith(2, 3);

92

93

// Override implementation

94

addSpy.mockImplementation((a, b) => a * b);

95

const multiplied = calc.add(2, 3);

96

expect(multiplied).toBe(6); // Now it multiplies

97

98

// Restore original behavior

99

addSpy.mockRestore();

100

const restored = calc.add(2, 3);

101

expect(restored).toBe(5); // Back to addition

102

},

103

};

104

```

105

106

### Mock Instance Interface

107

108

All mock functions implement the MockInstance interface with comprehensive tracking and configuration methods.

109

110

```typescript { .api }

111

interface MockInstance<TArgs extends any[] = any[], TReturns = any> {

112

/**

113

* Get the current mock name for debugging

114

*/

115

getMockName(): string;

116

117

/**

118

* Set a name for the mock for debugging purposes

119

* @param name - Name to assign to the mock

120

*/

121

mockName(name: string): this;

122

123

/**

124

* Clear all call history but preserve implementation

125

*/

126

mockClear(): this;

127

128

/**

129

* Reset mock to initial state (clear calls and implementation)

130

*/

131

mockReset(): this;

132

133

/**

134

* Restore original implementation (for spies)

135

*/

136

mockRestore(): void;

137

138

/**

139

* Set a custom implementation for the mock

140

* @param fn - Implementation function

141

*/

142

mockImplementation(fn?: (...args: TArgs) => TReturns): this;

143

144

/**

145

* Set implementation for the next single call only

146

* @param fn - Implementation function for one call

147

*/

148

mockImplementationOnce(fn: (...args: TArgs) => TReturns): this;

149

150

/**

151

* Set return value for all calls

152

* @param value - Value to return

153

*/

154

mockReturnValue(value: TReturns): this;

155

156

/**

157

* Set return value for the next single call only

158

* @param value - Value to return for one call

159

*/

160

mockReturnValueOnce(value: TReturns): this;

161

162

/**

163

* Set resolved value for async mocks

164

* @param value - Value to resolve with

165

*/

166

mockResolvedValue(value: Awaited<TReturns>): this;

167

168

/**

169

* Set resolved value for the next single call only

170

* @param value - Value to resolve with for one call

171

*/

172

mockResolvedValueOnce(value: Awaited<TReturns>): this;

173

174

/**

175

* Set rejected value for async mocks

176

* @param value - Value to reject with

177

*/

178

mockRejectedValue(value: any): this;

179

180

/**

181

* Set rejected value for the next single call only

182

* @param value - Value to reject with for one call

183

*/

184

mockRejectedValueOnce(value: any): this;

185

186

// Call tracking properties

187

mock: {

188

calls: TArgs[];

189

results: Array<{ type: 'return' | 'throw'; value: TReturns }>;

190

instances: any[];

191

contexts: any[];

192

lastCall?: TArgs;

193

};

194

}

195

```

196

197

**Usage Examples:**

198

199

```typescript

200

import { fn, expect } from '@storybook/test';

201

202

export const MockInstanceStory = {

203

play: async () => {

204

const mock = fn<[number, number], number>();

205

206

// Configure return values

207

mock.mockReturnValue(42);

208

expect(mock(1, 2)).toBe(42);

209

210

mock.mockReturnValueOnce(100);

211

expect(mock(3, 4)).toBe(100); // One-time return

212

expect(mock(5, 6)).toBe(42); // Back to default

213

214

// Configure implementation

215

mock.mockImplementation((a, b) => a + b);

216

expect(mock(10, 20)).toBe(30);

217

218

// One-time implementation

219

mock.mockImplementationOnce((a, b) => a * b);

220

expect(mock(3, 4)).toBe(12); // Multiply once

221

expect(mock(3, 4)).toBe(7); // Back to addition

222

223

// Async mocks

224

const asyncMock = fn<[], Promise<string>>();

225

asyncMock.mockResolvedValue('success');

226

await expect(asyncMock()).resolves.toBe('success');

227

228

asyncMock.mockRejectedValueOnce('error');

229

await expect(asyncMock()).rejects.toBe('error');

230

await expect(asyncMock()).resolves.toBe('success');

231

},

232

};

233

```

234

235

### Mock Lifecycle Management

236

237

Utilities for managing mocks across stories and test scenarios.

238

239

```typescript { .api }

240

/**

241

* Clear call history for all active mocks

242

* Calls .mockClear() on every mock

243

*/

244

function clearAllMocks(): void;

245

246

/**

247

* Reset all active mocks to initial state

248

* Calls .mockReset() on every mock

249

*/

250

function resetAllMocks(): void;

251

252

/**

253

* Restore all spied methods to original implementations

254

* Calls .mockRestore() on every mock

255

*/

256

function restoreAllMocks(): void;

257

258

/**

259

* Check if a value is a mock function

260

* @param value - Value to check

261

* @returns True if value is a mock function

262

*/

263

function isMockFunction(value: unknown): boolean;

264

265

/**

266

* Set of all active mock instances

267

*/

268

const mocks: Set<MockInstance>;

269

```

270

271

**Usage Examples:**

272

273

```typescript

274

import { fn, spyOn, clearAllMocks, resetAllMocks, restoreAllMocks, isMockFunction } from '@storybook/test';

275

276

const originalConsole = console.log;

277

278

export const LifecycleStory = {

279

play: async () => {

280

// Create various mocks

281

const mockFn = fn();

282

const consoleSpy = spyOn(console, 'log');

283

284

mockFn('test');

285

console.log('test message');

286

287

// Check if something is a mock

288

expect(isMockFunction(mockFn)).toBe(true);

289

expect(isMockFunction(console.log)).toBe(true);

290

expect(isMockFunction(originalConsole)).toBe(false);

291

292

// Clear all call history

293

clearAllMocks();

294

expect(mockFn).not.toHaveBeenCalled();

295

expect(consoleSpy).not.toHaveBeenCalled();

296

297

// Reset all mocks (clears history and implementations)

298

mockFn.mockReturnValue('custom');

299

resetAllMocks();

300

expect(mockFn()).toBeUndefined(); // Implementation reset

301

302

// Restore original implementations

303

restoreAllMocks();

304

console.log('this works normally again');

305

},

306

};

307

```

308

309

### Mock Call Listeners

310

311

Listen to mock function calls for advanced debugging and interaction tracking.

312

313

```typescript { .api }

314

/**

315

* Register a listener for all mock function calls

316

* @param callback - Function called when any mock is invoked

317

* @returns Cleanup function to remove the listener

318

*/

319

function onMockCall(callback: Listener): () => void;

320

321

type Listener = (mock: MockInstance, args: unknown[]) => void;

322

```

323

324

**Usage Examples:**

325

326

```typescript

327

import { fn, onMockCall } from '@storybook/test';

328

329

export const ListenerStory = {

330

play: async () => {

331

const calls: Array<{ mock: MockInstance; args: unknown[] }> = [];

332

333

// Listen to all mock calls

334

const unsubscribe = onMockCall((mock, args) => {

335

calls.push({ mock, args });

336

console.log(`Mock ${mock.getMockName()} called with:`, args);

337

});

338

339

const mockA = fn().mockName('mockA');

340

const mockB = fn().mockName('mockB');

341

342

mockA('arg1');

343

mockB('arg2', 'arg3');

344

345

expect(calls).toHaveLength(2);

346

expect(calls[0].args).toEqual(['arg1']);

347

expect(calls[1].args).toEqual(['arg2', 'arg3']);

348

349

// Clean up listener

350

unsubscribe();

351

352

mockA('this will not be logged');

353

expect(calls).toHaveLength(2); // No new calls logged

354

},

355

};

356

```

357

358

### Type Helpers for Mocking

359

360

Utility types for working with mocked objects and maintaining type safety.

361

362

```typescript { .api }

363

/**

364

* Type helper for objects that may have mocked methods

365

* @param item - Object to type as potentially mocked

366

* @param deep - Whether to deeply mock nested objects (default: false)

367

* @param options - Configuration for mocking behavior

368

* @returns The same object with mock typing

369

*/

370

function mocked<T>(item: T, deep?: false): MaybeMocked<T>;

371

function mocked<T>(item: T, deep: true): MaybeMockedDeep<T>;

372

function mocked<T>(item: T, options: { partial?: false; deep?: false }): MaybeMocked<T>;

373

function mocked<T>(item: T, options: { partial?: false; deep: true }): MaybeMockedDeep<T>;

374

function mocked<T>(item: T, options: { partial: true; deep?: false }): MaybePartiallyMocked<T>;

375

function mocked<T>(item: T, options: { partial: true; deep: true }): MaybePartiallyMockedDeep<T>;

376

377

type MaybeMocked<T> = T & {

378

[K in keyof T]: T[K] extends (...args: any[]) => any

379

? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>

380

: T[K];

381

};

382

383

type MaybeMockedDeep<T> = T & {

384

[K in keyof T]: T[K] extends (...args: any[]) => any

385

? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>

386

: MaybeMockedDeep<T[K]>;

387

};

388

389

type MaybePartiallyMocked<T> = {

390

[K in keyof T]?: T[K] extends (...args: any[]) => any

391

? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>

392

: T[K];

393

};

394

395

type MaybePartiallyMockedDeep<T> = {

396

[K in keyof T]?: T[K] extends (...args: any[]) => any

397

? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>

398

: MaybePartiallyMockedDeep<T[K]>;

399

};

400

```

401

402

**Usage Examples:**

403

404

```typescript

405

import { mocked, spyOn } from '@storybook/test';

406

407

interface ApiService {

408

fetchUser(id: string): Promise<User>;

409

updateUser(user: User): Promise<void>;

410

nested: {

411

helper(data: any): string;

412

};

413

}

414

415

export const TypeHelpersStory = {

416

play: async () => {

417

const apiService: ApiService = {

418

fetchUser: async (id) => ({ id, name: 'John' }),

419

updateUser: async (user) => {},

420

nested: {

421

helper: (data) => JSON.stringify(data),

422

},

423

};

424

425

// Spy on methods and use type helper

426

spyOn(apiService, 'fetchUser');

427

spyOn(apiService, 'updateUser');

428

429

// Type helper provides correct typing for mocked methods

430

const mockedService = mocked(apiService);

431

mockedService.fetchUser.mockResolvedValue({ id: '1', name: 'Mock User' });

432

433

const user = await apiService.fetchUser('1');

434

expect(user.name).toBe('Mock User');

435

436

// Deep mocking for nested objects

437

const deepMocked = mocked(apiService, { deep: true });

438

// Now deepMocked.nested.helper is also typed as a mock

439

},

440

};

441

```