0
# Spy and Mock Matchers
1
2
Verification matchers for Jest mock functions and spies, essential for testing function calls and interactions. These matchers allow you to verify that functions were called with the correct arguments, the right number of times, and in the expected order.
3
4
## Capabilities
5
6
### Basic Call Verification
7
8
#### toBeCalled / toHaveBeenCalled
9
10
Verifies that a mock function was called at least once.
11
12
```javascript { .api }
13
/**
14
* Checks if mock function was called at least once
15
*/
16
ExpectationObject.toBeCalled(): void;
17
18
/**
19
* Alias for toBeCalled - checks if mock function was called
20
*/
21
ExpectationObject.toHaveBeenCalled(): void;
22
```
23
24
**Usage Examples:**
25
26
```javascript
27
// Basic mock function testing
28
const mockFn = jest.fn();
29
mockFn();
30
expect(mockFn).toBeCalled();
31
expect(mockFn).toHaveBeenCalled(); // same as toBeCalled
32
33
// Testing event handlers
34
const onClickHandler = jest.fn();
35
const button = {
36
click: () => onClickHandler()
37
};
38
39
button.click();
40
expect(onClickHandler).toHaveBeenCalled();
41
42
// Testing callback functions
43
function processData(data, callback) {
44
// process data...
45
callback(data);
46
}
47
48
const callback = jest.fn();
49
processData({id: 1}, callback);
50
expect(callback).toHaveBeenCalled();
51
```
52
53
### Argument Verification
54
55
#### toBeCalledWith / toHaveBeenCalledWith
56
57
Verifies that a mock function was called with specific arguments at least once.
58
59
```javascript { .api }
60
/**
61
* Checks if mock function was called with specific arguments
62
* @param args - Expected arguments the function should have been called with
63
*/
64
ExpectationObject.toBeCalledWith(...args: any[]): void;
65
66
/**
67
* Alias for toBeCalledWith - checks function was called with specific arguments
68
* @param args - Expected arguments the function should have been called with
69
*/
70
ExpectationObject.toHaveBeenCalledWith(...args: any[]): void;
71
```
72
73
**Usage Examples:**
74
75
```javascript
76
const mockFn = jest.fn();
77
mockFn('hello', 42, {active: true});
78
79
expect(mockFn).toBeCalledWith('hello', 42, {active: true});
80
expect(mockFn).toHaveBeenCalledWith('hello', 42, {active: true});
81
82
// Testing API calls
83
const apiClient = {
84
get: jest.fn()
85
};
86
87
await apiClient.get('/users', {limit: 10});
88
expect(apiClient.get).toHaveBeenCalledWith('/users', {limit: 10});
89
90
// Testing with partial matching using asymmetric matchers
91
const logger = jest.fn();
92
logger('Error', 'Something went wrong', {timestamp: new Date()});
93
94
expect(logger).toHaveBeenCalledWith(
95
'Error',
96
'Something went wrong',
97
expect.objectContaining({timestamp: expect.any(Date)})
98
);
99
```
100
101
### Call Count Verification
102
103
#### toHaveBeenCalledTimes
104
105
Verifies that a mock function was called a specific number of times.
106
107
```javascript { .api }
108
/**
109
* Checks if mock function was called specific number of times
110
* @param number - Expected number of calls
111
*/
112
ExpectationObject.toHaveBeenCalledTimes(number: number): void;
113
```
114
115
**Usage Examples:**
116
117
```javascript
118
const mockFn = jest.fn();
119
120
// Initially not called
121
expect(mockFn).toHaveBeenCalledTimes(0);
122
123
mockFn();
124
expect(mockFn).toHaveBeenCalledTimes(1);
125
126
mockFn();
127
mockFn();
128
expect(mockFn).toHaveBeenCalledTimes(3);
129
130
// Testing initialization functions
131
const initializeApp = jest.fn();
132
const app = new Application();
133
app.init();
134
135
expect(initializeApp).toHaveBeenCalledTimes(1);
136
137
// Testing event listeners
138
const eventHandler = jest.fn();
139
const emitter = new EventEmitter();
140
emitter.on('data', eventHandler);
141
142
emitter.emit('data', 'test1');
143
emitter.emit('data', 'test2');
144
145
expect(eventHandler).toHaveBeenCalledTimes(2);
146
```
147
148
### Last Call Verification
149
150
#### lastCalledWith / toHaveBeenLastCalledWith
151
152
Verifies the arguments of the most recent call to a mock function.
153
154
```javascript { .api }
155
/**
156
* Checks if mock function was last called with specific arguments
157
* @param args - Expected arguments from the most recent call
158
*/
159
ExpectationObject.lastCalledWith(...args: any[]): void;
160
161
/**
162
* Alias for lastCalledWith - checks most recent call arguments
163
* @param args - Expected arguments from the most recent call
164
*/
165
ExpectationObject.toHaveBeenLastCalledWith(...args: any[]): void;
166
```
167
168
**Usage Examples:**
169
170
```javascript
171
const mockFn = jest.fn();
172
173
mockFn('first call');
174
mockFn('second call');
175
mockFn('third call');
176
177
expect(mockFn).lastCalledWith('third call');
178
expect(mockFn).toHaveBeenLastCalledWith('third call');
179
180
// Testing progressive updates
181
const updateProgress = jest.fn();
182
183
updateProgress('Starting...');
184
updateProgress('Processing...', 50);
185
updateProgress('Completed!', 100);
186
187
expect(updateProgress).toHaveBeenLastCalledWith('Completed!', 100);
188
expect(updateProgress).toHaveBeenCalledTimes(3);
189
190
// Testing state changes
191
const setState = jest.fn();
192
193
setState({loading: true});
194
setState({loading: false, data: []});
195
setState({loading: false, data: [1, 2, 3]});
196
197
expect(setState).toHaveBeenLastCalledWith({
198
loading: false,
199
data: [1, 2, 3]
200
});
201
```
202
203
## Advanced Usage Patterns
204
205
### Testing Function Compositions
206
207
```javascript
208
const mockTransform = jest.fn(x => x * 2);
209
const mockFilter = jest.fn(x => x > 5);
210
const mockMap = jest.fn(x => x.toString());
211
212
const pipeline = [1, 2, 3, 4, 5, 6]
213
.map(mockTransform)
214
.filter(mockFilter)
215
.map(mockMap);
216
217
// Verify each function was called the right number of times
218
expect(mockTransform).toHaveBeenCalledTimes(6);
219
expect(mockFilter).toHaveBeenCalledTimes(6);
220
expect(mockMap).toHaveBeenCalledTimes(3); // only items > 5 after transform
221
222
// Verify specific calls
223
expect(mockTransform).toHaveBeenCalledWith(1);
224
expect(mockTransform).toHaveBeenCalledWith(6);
225
expect(mockFilter).toHaveBeenCalledWith(12); // 6 * 2
226
expect(mockMap).toHaveBeenLastCalledWith(12);
227
```
228
229
### Testing Async Operations
230
231
```javascript
232
const mockCallback = jest.fn();
233
234
async function processAsync(items, callback) {
235
for (const item of items) {
236
await new Promise(resolve => setTimeout(resolve, 10));
237
callback(item);
238
}
239
}
240
241
await processAsync(['a', 'b', 'c'], mockCallback);
242
243
expect(mockCallback).toHaveBeenCalledTimes(3);
244
expect(mockCallback).toHaveBeenNthCalledWith(1, 'a');
245
expect(mockCallback).toHaveBeenNthCalledWith(2, 'b');
246
expect(mockCallback).toHaveBeenLastCalledWith('c');
247
```
248
249
### Testing Error Handling
250
251
```javascript
252
const mockErrorHandler = jest.fn();
253
const mockSuccessHandler = jest.fn();
254
255
function processWithHandlers(data) {
256
try {
257
if (!data) throw new Error('No data provided');
258
mockSuccessHandler(data);
259
} catch (error) {
260
mockErrorHandler(error);
261
}
262
}
263
264
// Test error case
265
processWithHandlers(null);
266
expect(mockErrorHandler).toHaveBeenCalledWith(
267
expect.objectContaining({message: 'No data provided'})
268
);
269
expect(mockSuccessHandler).not.toHaveBeenCalled();
270
271
// Reset mocks
272
mockErrorHandler.mockReset();
273
mockSuccessHandler.mockReset();
274
275
// Test success case
276
processWithHandlers({id: 1});
277
expect(mockSuccessHandler).toHaveBeenCalledWith({id: 1});
278
expect(mockErrorHandler).not.toHaveBeenCalled();
279
```
280
281
### Testing Class Methods
282
283
```javascript
284
class UserService {
285
constructor(apiClient) {
286
this.apiClient = apiClient;
287
}
288
289
async createUser(userData) {
290
const user = await this.apiClient.post('/users', userData);
291
this.logActivity('user_created', user.id);
292
return user;
293
}
294
295
logActivity(action, userId) {
296
// logging implementation
297
}
298
}
299
300
// Test with mocked dependencies
301
const mockApiClient = {
302
post: jest.fn().mockResolvedValue({id: 123, name: 'John'})
303
};
304
305
const userService = new UserService(mockApiClient);
306
userService.logActivity = jest.fn();
307
308
const userData = {name: 'John', email: 'john@example.com'};
309
const result = await userService.createUser(userData);
310
311
expect(mockApiClient.post).toHaveBeenCalledWith('/users', userData);
312
expect(userService.logActivity).toHaveBeenCalledWith('user_created', 123);
313
expect(mockApiClient.post).toHaveBeenCalledTimes(1);
314
```
315
316
## Integration with Asymmetric Matchers
317
318
Spy matchers work well with asymmetric matchers for flexible argument verification:
319
320
```javascript
321
const mockFn = jest.fn();
322
323
mockFn('test', {timestamp: new Date(), id: 123});
324
325
expect(mockFn).toHaveBeenCalledWith(
326
expect.any(String),
327
expect.objectContaining({
328
timestamp: expect.any(Date),
329
id: expect.any(Number)
330
})
331
);
332
```
333
334
## Negation Support
335
336
All spy matchers support negation:
337
338
```javascript
339
const mockFn = jest.fn();
340
341
expect(mockFn).not.toHaveBeenCalled();
342
343
mockFn('test');
344
expect(mockFn).not.toHaveBeenCalledWith('different');
345
expect(mockFn).not.toHaveBeenCalledTimes(2);
346
```
347
348
## Promise Support
349
350
Spy matchers work with promises when testing async mock functions:
351
352
```javascript
353
const asyncMock = jest.fn().mockResolvedValue('result');
354
355
await asyncMock('test');
356
357
await expect(Promise.resolve(asyncMock)).resolves.toHaveBeenCalledWith('test');
358
```