0
# Assertions and Expectations
1
2
Jest-compatible assertion APIs powered by Chai with extensive matchers and custom matcher support.
3
4
## Expect API
5
6
```typescript { .api }
7
function expect<T>(actual: T): Assertion<T>;
8
9
interface ExpectStatic {
10
<T>(actual: T): Assertion<T>;
11
extend(matchers: Record<string, CustomMatcher>): void;
12
addEqualityTesters(testers: Array<Tester>): void;
13
getState(): MatcherState;
14
setState(state: Partial<MatcherState>): void;
15
unreachable(message?: string): never;
16
hasAssertions(): void;
17
assertions(count: number): void;
18
soft<T>(actual: T, message?: string): Assertion<T>;
19
poll<T>(fn: () => T | Promise<T>, options?: ExpectPollOptions): Promise<Assertion<T>>;
20
}
21
22
interface ExpectPollOptions {
23
interval?: number; // default: 50ms
24
timeout?: number; // default: 1000ms
25
message?: string;
26
}
27
```
28
29
## Core Matchers
30
31
### Equality
32
33
| Matcher | Purpose | Example |
34
|---------|---------|---------|
35
| `.toBe(expected)` | Strict equality (===) | `expect(x).toBe(5)` |
36
| `.toEqual(expected)` | Deep equality | `expect({a:1}).toEqual({a:1})` |
37
| `.toStrictEqual(expected)` | Strict deep equality | `expect({a:1,b:undefined}).toStrictEqual({a:1,b:undefined})` |
38
39
### Truthiness
40
41
```typescript { .api }
42
.toBeTruthy() // Truthy value
43
.toBeFalsy() // Falsy value
44
.toBeDefined() // Not undefined
45
.toBeUndefined() // Is undefined
46
.toBeNull() // Is null
47
.toBeNaN() // Is NaN
48
```
49
50
### Numbers
51
52
```typescript { .api }
53
.toBeGreaterThan(n)
54
.toBeGreaterThanOrEqual(n)
55
.toBeLessThan(n)
56
.toBeLessThanOrEqual(n)
57
.toBeCloseTo(n, digits?) // Floating point comparison
58
```
59
60
**Example:**
61
```typescript
62
expect(10).toBeGreaterThan(5);
63
expect(0.1 + 0.2).toBeCloseTo(0.3); // Handles floating point
64
```
65
66
### Strings
67
68
```typescript { .api }
69
.toMatch(regexp | string) // Matches pattern
70
.toContain(substring) // Contains substring
71
```
72
73
### Arrays/Iterables
74
75
```typescript { .api }
76
.toContain(item) // Array/string contains item
77
.toContainEqual(item) // Deep equality check for item
78
.toHaveLength(n) // Length equals n
79
```
80
81
**Example:**
82
```typescript
83
expect([1, 2, 3]).toContain(2);
84
expect([{id: 1}, {id: 2}]).toContainEqual({id: 1});
85
expect([1, 2, 3]).toHaveLength(3);
86
```
87
88
### Objects
89
90
```typescript { .api }
91
.toHaveProperty(keyPath, value?) // Has property at path
92
.toMatchObject(subset) // Partial object match
93
```
94
95
**Example:**
96
```typescript
97
expect({a: 1, b: {c: 2}}).toHaveProperty('b.c', 2);
98
expect({a: 1, b: 2, c: 3}).toMatchObject({a: 1, b: 2});
99
```
100
101
### Functions/Errors
102
103
```typescript { .api }
104
.toThrow(expected?) // Throws error
105
.toThrowError(expected?) // Alias for toThrow
106
```
107
108
**Example:**
109
```typescript
110
expect(() => { throw new Error('fail') }).toThrow('fail');
111
expect(() => { throw new Error('fail') }).toThrow(/fail/);
112
```
113
114
### Promises
115
116
```typescript { .api }
117
.resolves // Access resolved promise value
118
.rejects // Access rejected promise error
119
```
120
121
**Example:**
122
```typescript
123
await expect(Promise.resolve('success')).resolves.toBe('success');
124
await expect(Promise.reject(new Error('fail'))).rejects.toThrow('fail');
125
```
126
127
### Modifiers
128
129
```typescript { .api }
130
.not // Negate assertion
131
```
132
133
**Example:**
134
```typescript
135
expect(1).not.toBe(2);
136
expect('hello').not.toMatch(/world/);
137
```
138
139
## Mock Function Matchers
140
141
```typescript { .api }
142
// Call assertions
143
.toHaveBeenCalled() // Called at least once
144
.toBeCalled() // Alias
145
.toHaveBeenCalledTimes(n) // Called exactly n times
146
.toHaveBeenCalledWith(...args) // Called with specific args
147
.toHaveBeenLastCalledWith(...args) // Last call had specific args
148
.toHaveBeenNthCalledWith(n, ...args) // Nth call had specific args
149
150
// Return assertions
151
.toHaveReturned() // Returned successfully
152
.toHaveReturnedTimes(n) // Returned n times
153
.toHaveReturnedWith(value) // Returned specific value
154
.toHaveLastReturnedWith(value) // Last return was specific value
155
.toHaveNthReturnedWith(n, value) // Nth return was specific value
156
```
157
158
**Example:**
159
```typescript
160
const mockFn = vi.fn((x) => x * 2);
161
mockFn(1); mockFn(2); mockFn(3);
162
163
expect(mockFn).toHaveBeenCalledTimes(3);
164
expect(mockFn).toHaveBeenCalledWith(2);
165
expect(mockFn).toHaveBeenNthCalledWith(2, 2);
166
expect(mockFn).toHaveReturnedWith(4);
167
expect(mockFn).toHaveLastReturnedWith(6);
168
```
169
170
## Snapshot Testing
171
172
```typescript { .api }
173
.toMatchSnapshot(message?) // Compare to saved snapshot
174
.toMatchInlineSnapshot(snapshot?, message?) // Inline snapshot in code
175
.toMatchFileSnapshot(filepath, message?) // Compare to file
176
.toThrowErrorMatchingSnapshot(message?) // Error snapshot
177
.toThrowErrorMatchingInlineSnapshot(snapshot?) // Inline error snapshot
178
```
179
180
**Example:**
181
```typescript
182
expect({id: 1, name: 'John'}).toMatchSnapshot();
183
184
expect({success: true, count: 5}).toMatchInlineSnapshot(`
185
{
186
"success": true,
187
"count": 5
188
}
189
`);
190
191
await expect('<div>Hello</div>').toMatchFileSnapshot('./snapshots/output.html');
192
```
193
194
## Advanced Features
195
196
### Soft Assertions
197
198
Continue test execution even when assertions fail.
199
200
```typescript
201
expect.soft(1 + 1).toBe(2);
202
expect.soft(2 + 2).toBe(5); // Fails but continues
203
expect.soft(3 + 3).toBe(6);
204
// Test reports all failures at end
205
```
206
207
### Polling Assertions
208
209
Retry assertions until condition is met.
210
211
```typescript
212
let value = 0;
213
setTimeout(() => { value = 10 }, 100);
214
215
await expect.poll(() => value, {
216
interval: 20,
217
timeout: 200
218
}).toBe(10);
219
220
// Async functions
221
await expect.poll(
222
async () => {
223
const res = await fetch('/api/status');
224
return res.json();
225
},
226
{ timeout: 5000 }
227
).toMatchObject({ status: 'ready' });
228
```
229
230
### Custom Matchers
231
232
```typescript { .api }
233
expect.extend(matchers: Record<string, CustomMatcher>): void;
234
235
interface CustomMatcher {
236
(this: MatcherContext, actual: any, ...expected: any[]): MatcherResult;
237
}
238
239
interface MatcherResult {
240
pass: boolean;
241
message: () => string;
242
actual?: any;
243
expected?: any;
244
}
245
```
246
247
**Example:**
248
```typescript
249
expect.extend({
250
toBeWithinRange(received: number, floor: number, ceiling: number) {
251
const pass = received >= floor && received <= ceiling;
252
return {
253
pass,
254
message: () => pass
255
? `expected ${received} not to be within ${floor}-${ceiling}`
256
: `expected ${received} to be within ${floor}-${ceiling}`,
257
};
258
},
259
});
260
261
expect(15).toBeWithinRange(10, 20);
262
expect(25).not.toBeWithinRange(10, 20);
263
```
264
265
### Assertion Count
266
267
```typescript
268
// Require exact number of assertions
269
test('exact count', () => {
270
expect.assertions(2);
271
expect(1).toBe(1);
272
expect(2).toBe(2);
273
// Fails if != 2 assertions
274
});
275
276
// Require at least one assertion
277
test('at least one', async () => {
278
expect.hasAssertions();
279
const data = await fetchData();
280
if (data) expect(data).toBeDefined();
281
// Fails if no assertions run
282
});
283
```
284
285
### Create Custom Expect
286
287
```typescript { .api }
288
function createExpect(test?: TaskPopulated): ExpectStatic;
289
```
290
291
**Example:**
292
```typescript
293
const customExpect = createExpect();
294
customExpect.extend({
295
toBeDivisibleBy(received: number, divisor: number) {
296
return {
297
pass: received % divisor === 0,
298
message: () => `expected ${received} to be divisible by ${divisor}`,
299
};
300
},
301
});
302
303
customExpect(10).toBeDivisibleBy(5);
304
```
305
306
## Asymmetric Matchers
307
308
Partial matching for complex objects.
309
310
```typescript { .api }
311
expect.any(constructor) // Match any instance of type
312
expect.anything() // Match anything except null/undefined
313
expect.arrayContaining(items) // Array contains items
314
expect.objectContaining(obj) // Object contains properties
315
expect.stringContaining(str) // String contains substring
316
expect.stringMatching(regexp) // String matches pattern
317
expect.closeTo(n, precision?) // Number close to value
318
```
319
320
**Example:**
321
```typescript
322
expect({id: 1, name: 'John', age: 30}).toEqual({
323
id: expect.any(Number),
324
name: expect.any(String),
325
age: expect.any(Number)
326
});
327
328
expect([1, 2, 3, 4]).toEqual(expect.arrayContaining([2, 3]));
329
expect({a: 1, b: 2, c: 3}).toEqual(expect.objectContaining({a: 1}));
330
expect('hello world').toEqual(expect.stringContaining('world'));
331
expect('test123').toEqual(expect.stringMatching(/test\d+/));
332
```
333
334
## Chai API
335
336
Vitest re-exports Chai for alternative assertion styles.
337
338
```typescript { .api }
339
const assert: Chai.AssertStatic;
340
const chai: Chai.ChaiStatic;
341
const should: Chai.Should;
342
```
343
344
**Example:**
345
```typescript
346
import { assert, should } from 'vitest';
347
348
// Node.js assert style
349
assert.equal(1 + 1, 2);
350
assert.isTrue(true);
351
assert.deepEqual({a: 1}, {a: 1});
352
353
// Chai should style
354
should();
355
(1 + 1).should.equal(2);
356
'hello'.should.be.a('string');
357
[1, 2, 3].should.have.lengthOf(3);
358
```
359
360
## Type Definitions
361
362
```typescript { .api }
363
interface Assertion<T> {
364
not: Assertion<T>;
365
resolves: Assertion<Awaited<T>>;
366
rejects: Assertion<any>;
367
368
toBe(expected: any): void;
369
toEqual(expected: any): void;
370
toStrictEqual(expected: any): void;
371
toBeTruthy(): void;
372
toBeFalsy(): void;
373
toBeDefined(): void;
374
toBeUndefined(): void;
375
toBeNull(): void;
376
toBeNaN(): void;
377
toBeTypeOf(expected: string): void;
378
toBeInstanceOf(expected: any): void;
379
toBeGreaterThan(expected: number | bigint): void;
380
toBeGreaterThanOrEqual(expected: number | bigint): void;
381
toBeLessThan(expected: number | bigint): void;
382
toBeLessThanOrEqual(expected: number | bigint): void;
383
toBeCloseTo(expected: number, numDigits?: number): void;
384
toMatch(expected: string | RegExp): void;
385
toContain(expected: any): void;
386
toContainEqual(expected: any): void;
387
toHaveLength(expected: number): void;
388
toHaveProperty(keyPath: string | string[], value?: any): void;
389
toMatchObject(expected: object): void;
390
toThrow(expected?: string | RegExp | Error): void;
391
toSatisfy(predicate: (value: any) => boolean): void;
392
toMatchSnapshot(message?: string): void;
393
toMatchInlineSnapshot(snapshot?: string, message?: string): void;
394
toMatchFileSnapshot(filepath: string, message?: string): Promise<void>;
395
}
396
397
interface MatcherState {
398
assertionCalls: number;
399
isExpectingAssertions: boolean;
400
expectedAssertionsNumber: number | null;
401
environment: Environment;
402
testPath?: string;
403
currentTestName?: string;
404
}
405
```
406
407
## Matcher Selection Guide
408
409
| Need | Use | Example |
410
|------|-----|---------|
411
| Primitive equality | `.toBe()` | `expect(5).toBe(5)` |
412
| Object equality | `.toEqual()` | `expect({a:1}).toEqual({a:1})` |
413
| Partial object | `.toMatchObject()` | `expect(obj).toMatchObject({a:1})` |
414
| Type check | `.any()` | `expect.any(String)` |
415
| Range check | Custom matcher | `toBe WithinRange()` |
416
| Async success | `.resolves` | `expect(p).resolves.toBe(x)` |
417
| Async failure | `.rejects` | `expect(p).rejects.toThrow()` |
418
| Function called | `.toHaveBeenCalled()` | Mock assertions |
419
| Contains item | `.toContain()` | Array/string search |
420
| Has property | `.toHaveProperty()` | Object property |
421
| Pattern match | `.toMatch()` | String/regex |
422