or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-mock-generation.mdindex.mdmock-functions.mdspy-operations.mdtype-system.md
tile.json

spy-operations.mddocs/

0

# Spy Operations

1

2

Non-invasive spying and property replacement capabilities for observing and temporarily modifying existing objects without permanent changes.

3

4

## Capabilities

5

6

### Method Spying

7

8

Creates spies on existing object methods to track calls while preserving original behavior.

9

10

```typescript { .api }

11

/**

12

* Creates a spy on an object method

13

* @param object - Target object containing the method

14

* @param methodKey - Name of the method to spy on

15

* @returns MockInstance for the spied method

16

*/

17

function spyOn<T extends object, K extends MethodLikeKeys<T>>(

18

object: T,

19

methodKey: K

20

): Spied<Required<T>[K]>;

21

22

/**

23

* Creates a spy on a property accessor (getter or setter)

24

* @param object - Target object containing the property

25

* @param propertyKey - Name of the property to spy on

26

* @param accessType - Whether to spy on 'get' or 'set' accessor

27

* @returns MockInstance for the property accessor

28

*/

29

function spyOn<T extends object, K extends PropertyLikeKeys<T>, A extends 'get' | 'set'>(

30

object: T,

31

propertyKey: K,

32

accessType: A

33

): A extends 'get' ? SpiedGetter<Required<T>[K]> : SpiedSetter<Required<T>[K]>;

34

35

type MethodLikeKeys<T> = keyof {

36

[K in keyof T as Required<T>[K] extends FunctionLike ? K : never]: T[K];

37

};

38

39

type PropertyLikeKeys<T> = Exclude<keyof T, ConstructorLikeKeys<T> | MethodLikeKeys<T>>;

40

41

type Spied<T extends ClassLike | FunctionLike> = T extends ClassLike

42

? SpiedClass<T>

43

: T extends FunctionLike

44

? SpiedFunction<T>

45

: never;

46

47

type SpiedFunction<T extends FunctionLike = UnknownFunction> = MockInstance<(...args: Parameters<T>) => ReturnType<T>>;

48

type SpiedClass<T extends ClassLike = UnknownClass> = MockInstance<(...args: ConstructorParameters<T>) => InstanceType<T>>;

49

type SpiedGetter<T> = MockInstance<() => T>;

50

type SpiedSetter<T> = MockInstance<(arg: T) => void>;

51

```

52

53

**Usage Examples:**

54

55

```typescript

56

import { spyOn } from "jest-mock";

57

58

const calculator = {

59

add: (a: number, b: number) => a + b,

60

subtract: (a: number, b: number) => a - b,

61

history: [] as string[]

62

};

63

64

// Spy on a method

65

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

66

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

67

console.log(result); // 5 (original behavior preserved)

68

console.log(addSpy.mock.calls); // [[2, 3]]

69

70

// Override spy behavior

71

addSpy.mockReturnValue(42);

72

console.log(calculator.add(1, 1)); // 42

73

74

// Restore original behavior

75

addSpy.mockRestore();

76

console.log(calculator.add(1, 1)); // 2

77

```

78

79

### Property Spying

80

81

Creates spies on property getters and setters.

82

83

```typescript { .api }

84

/**

85

* Spy on property getter

86

*/

87

spyOn(object, propertyKey, 'get'): SpiedGetter<PropertyType>;

88

89

/**

90

* Spy on property setter

91

*/

92

spyOn(object, propertyKey, 'set'): SpiedSetter<PropertyType>;

93

```

94

95

**Usage Examples:**

96

97

```typescript

98

import { spyOn } from "jest-mock";

99

100

const config = {

101

_apiUrl: 'https://api.example.com',

102

get apiUrl() { return this._apiUrl; },

103

set apiUrl(value: string) { this._apiUrl = value; }

104

};

105

106

// Spy on getter

107

const getterSpy = spyOn(config, 'apiUrl', 'get');

108

console.log(config.apiUrl); // 'https://api.example.com'

109

console.log(getterSpy.mock.calls); // [[]]

110

111

// Override getter

112

getterSpy.mockReturnValue('https://test.example.com');

113

console.log(config.apiUrl); // 'https://test.example.com'

114

115

// Spy on setter

116

const setterSpy = spyOn(config, 'apiUrl', 'set');

117

config.apiUrl = 'https://new.example.com';

118

console.log(setterSpy.mock.calls); // [['https://new.example.com']]

119

```

120

121

### Property Replacement

122

123

Temporarily replaces object properties with new values, providing restoration capabilities.

124

125

```typescript { .api }

126

/**

127

* Temporarily replaces an object property with a new value

128

* @param object - Target object containing the property

129

* @param propertyKey - Name of the property to replace

130

* @param value - New value for the property

131

* @returns Replaced instance with control methods

132

*/

133

function replaceProperty<T extends object, K extends keyof T>(

134

object: T,

135

propertyKey: K,

136

value: T[K]

137

): Replaced<T[K]>;

138

139

interface Replaced<T = unknown> {

140

/** Restore property to its original value */

141

restore(): void;

142

/** Change the property to a new value */

143

replaceValue(value: T): this;

144

}

145

```

146

147

**Usage Examples:**

148

149

```typescript

150

import { replaceProperty } from "jest-mock";

151

152

const config = {

153

apiUrl: 'https://api.example.com',

154

timeout: 5000,

155

retries: 3

156

};

157

158

// Replace a property

159

const replaced = replaceProperty(config, 'apiUrl', 'https://test.example.com');

160

console.log(config.apiUrl); // 'https://test.example.com'

161

162

// Change to another value

163

replaced.replaceValue('https://staging.example.com');

164

console.log(config.apiUrl); // 'https://staging.example.com'

165

166

// Restore original value

167

replaced.restore();

168

console.log(config.apiUrl); // 'https://api.example.com'

169

170

// Replace multiple properties

171

const timeoutReplaced = replaceProperty(config, 'timeout', 1000);

172

const retriesReplaced = replaceProperty(config, 'retries', 1);

173

174

// Cleanup

175

timeoutReplaced.restore();

176

retriesReplaced.restore();

177

```

178

179

### Class Constructor Spying

180

181

Spy on class constructors to track instantiation.

182

183

```typescript { .api }

184

class MyClass {

185

constructor(public value: string) {}

186

method() { return this.value; }

187

}

188

189

// Spy on constructor

190

const ConstructorSpy = spyOn(MyClass.prototype, 'constructor');

191

const instance = new MyClass('test');

192

console.log(ConstructorSpy.mock.calls); // [['test']]

193

```

194

195

## Error Handling

196

197

Spy operations validate targets and throw descriptive errors for invalid usage:

198

199

```typescript

200

// Throws TypeError: Cannot spy on a primitive value

201

spyOn('not an object', 'method');

202

203

// Throws Error: Property 'nonexistent' does not exist

204

spyOn(obj, 'nonexistent');

205

206

// Throws TypeError: Cannot spy on property because it is not a function

207

spyOn(obj, 'stringProperty');

208

209

// Throws Error: Property is not declared configurable

210

const nonConfigurable = {};

211

Object.defineProperty(nonConfigurable, 'prop', { configurable: false, value: 'test' });

212

replaceProperty(nonConfigurable, 'prop', 'new');

213

```

214

215

## Advanced Usage

216

217

### Spy Restoration Patterns

218

219

```typescript

220

import { spyOn } from "jest-mock";

221

222

// Individual restoration

223

const spy1 = spyOn(obj, 'method1');

224

const spy2 = spyOn(obj, 'method2');

225

spy1.mockRestore();

226

spy2.mockRestore();

227

228

// Using ModuleMocker for bulk restoration

229

import { ModuleMocker } from "jest-mock";

230

const mocker = new ModuleMocker(globalThis);

231

const spy3 = mocker.spyOn(obj, 'method3');

232

const spy4 = mocker.spyOn(obj, 'method4');

233

mocker.restoreAllMocks(); // Restores all spies created by this mocker

234

```

235

236

### Spy Chaining

237

238

```typescript

239

import { spyOn } from "jest-mock";

240

241

const obj = {

242

getValue: () => 42,

243

chainable: function() { return this; }

244

};

245

246

// Chain spy operations

247

spyOn(obj, 'getValue')

248

.mockReturnValue(100)

249

.mockName('getValue spy');

250

251

spyOn(obj, 'chainable')

252

.mockReturnThis()

253

.mockName('chainable spy');

254

```

255

256

## Types

257

258

```typescript { .api }

259

type UnknownFunction = (...args: Array<unknown>) => unknown;

260

type UnknownClass = new (...args: Array<unknown>) => unknown;

261

type FunctionLike = (...args: any) => any;

262

type ClassLike = new (...args: any) => any;

263

264

type ConstructorLikeKeys<T> = keyof {

265

[K in keyof T as Required<T>[K] extends ClassLike ? K : never]: T[K];

266

};

267

```