strong-mock 8.0.0-beta.0 → 8.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -71
- package/dist/expectation/repository/expectation-repository.d.ts +17 -2
- package/dist/expectation/repository/flexible-repository.d.ts +37 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +189 -108
- package/dist/index.js.map +1 -1
- package/dist/mock/defaults.d.ts +3 -19
- package/dist/mock/map.d.ts +2 -0
- package/dist/mock/mock.d.ts +13 -15
- package/dist/mock/options.d.ts +72 -0
- package/dist/mock/stub.d.ts +3 -2
- package/dist/when/pending-expectation.d.ts +5 -3
- package/package.json +5 -5
- package/dist/expectation/repository/base-repository.d.ts +0 -41
- package/dist/expectation/repository/strong-repository.d.ts +0 -10
- package/dist/expectation/repository/weak-repository.d.ts +0 -17
package/README.md
CHANGED
|
@@ -44,14 +44,16 @@ console.log(foo.bar(23)); // 'I am strong!'
|
|
|
44
44
|
- [Verifying expectations](#verifying-expectations)
|
|
45
45
|
- [Resetting expectations](#resetting-expectations)
|
|
46
46
|
- [Argument matchers](#argument-matchers)
|
|
47
|
-
- [
|
|
47
|
+
- [Mock options](#mock-options)
|
|
48
|
+
- [Strictness](#strictness)
|
|
49
|
+
- [Concrete matcher](#concrete-matcher)
|
|
50
|
+
- [Defaults](#defaults)
|
|
48
51
|
- [FAQ](#faq)
|
|
49
52
|
- [Why do I have to set all expectations first?](#why-do-i-have-to-set-all-expectations-first)
|
|
50
|
-
- [Can I mock an existing object/function?](#can-i-mock-an-existing-objectfunction)
|
|
53
|
+
- [Can I partially mock an existing object/function?](#can-i-partially-mock-an-existing-objectfunction)
|
|
51
54
|
- [How do I set expectations on setters?](#how-do-i-set-expectations-on-setters)
|
|
52
55
|
- [Why do I have to set a return value even if it's `undefined`?](#why-do-i-have-to-set-a-return-value-even-if-its-undefined)
|
|
53
56
|
- [How do I provide a function for the mock to call?](#how-do-i-provide-a-function-for-the-mock-to-call)
|
|
54
|
-
- [Why does accessing an unused method throw?](#why-does-accessing-an-unused-method-throw)
|
|
55
57
|
- [Can I spread/enumerate a mock?](#can-i-spreadenumerate-a-mock)
|
|
56
58
|
- [How can I ignore `undefined` keys when setting expectations on objects?](#how-can-i-ignore-undefined-keys-when-setting-expectations-on-objects)
|
|
57
59
|
|
|
@@ -63,27 +65,37 @@ console.log(foo.bar(23)); // 'I am strong!'
|
|
|
63
65
|
|
|
64
66
|
The created mock matches the mocked type so all expectations are type safe. Moreover, refactorings in an IDE will also cover your expectations.
|
|
65
67
|
|
|
66
|
-

|
|
67
69
|
|
|
68
70
|
### Useful error messages
|
|
69
71
|
|
|
70
72
|
Error messages include the property that has been accessed, any arguments passed to it and any remaining unmet expectations.
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
```typescript
|
|
75
|
+
import { mock, when } from 'strong-mock';
|
|
76
|
+
|
|
77
|
+
const fn = mock<(a: number, b: number, c: number) => number>();
|
|
78
|
+
|
|
79
|
+
when(() => fn(1, 2, 3)).thenReturn(42);
|
|
80
|
+
|
|
81
|
+
fn(4, 5, 6);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+

|
|
73
85
|
|
|
74
86
|
### Type safe argument matchers
|
|
75
87
|
|
|
76
88
|
Optional argument matchers allow you to create complex expectations, while still maintaining type safety.
|
|
77
89
|
|
|
78
|
-

|
|
79
91
|
|
|
80
92
|
## Installation
|
|
81
93
|
|
|
82
|
-
```
|
|
94
|
+
```shell
|
|
83
95
|
npm i -D strong-mock
|
|
84
96
|
```
|
|
85
97
|
|
|
86
|
-
```
|
|
98
|
+
```shell
|
|
87
99
|
yarn add -D strong-mock
|
|
88
100
|
```
|
|
89
101
|
|
|
@@ -260,6 +272,12 @@ console.log(fn(
|
|
|
260
272
|
); // 'matched!'
|
|
261
273
|
```
|
|
262
274
|
|
|
275
|
+
You can mix argument matchers with concrete arguments:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
when(() => fn(42, It.isObject())).thenReturn('matched');
|
|
279
|
+
```
|
|
280
|
+
|
|
263
281
|
Available matchers:
|
|
264
282
|
- `deepEquals` - the default, uses deep equality,
|
|
265
283
|
- `is` - uses `Object.is` for comparison,
|
|
@@ -315,24 +333,67 @@ console.log(fn(23, (x) => x + 1)); // 42
|
|
|
315
333
|
console.log(matcher.value?.(3)); // 4
|
|
316
334
|
```
|
|
317
335
|
|
|
318
|
-
###
|
|
336
|
+
### Mock options
|
|
319
337
|
|
|
320
|
-
|
|
338
|
+
#### Strictness
|
|
321
339
|
|
|
322
|
-
|
|
323
|
-
import { mock, when, It, setDefaults } from 'strong-mock';
|
|
340
|
+
strong-mock has a few levels of "strictness" that control what values are returned when an unexpected property is accessed or an unexpected call is made. The strictness can be configured for each mock, or for all mocks with `setDefaults`.
|
|
324
341
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
342
|
+
```typescript
|
|
343
|
+
import { mock, when } from 'strong-mock';
|
|
344
|
+
import { Strictness } from './options';
|
|
345
|
+
|
|
346
|
+
type Foo = {
|
|
347
|
+
bar: (value: number) => number;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// This is the default.
|
|
351
|
+
const strictFoo = mock<Foo>({ strictness: Strictness.STRICT });
|
|
352
|
+
|
|
353
|
+
// Accessing properties with no expectations is fine.
|
|
354
|
+
strictFoo.bar;
|
|
355
|
+
// Throws "Didn't expect bar(42) to be called".
|
|
356
|
+
strictFoo.bar(42);
|
|
357
|
+
|
|
358
|
+
const superStrictFoo = mock<Foo>({ strictness: Strictness.SUPER_STRICT });
|
|
359
|
+
|
|
360
|
+
// Throws "Didn't expect property bar to be accessed".
|
|
361
|
+
superStrictFoo.bar;
|
|
362
|
+
// Throws "Didn't expect property bar to be accessed".
|
|
363
|
+
superStrictFoo.bar(42);
|
|
364
|
+
```
|
|
329
365
|
|
|
330
|
-
|
|
366
|
+
#### Concrete matcher
|
|
367
|
+
|
|
368
|
+
You can set the matcher that will be used in expectations with concrete values e.g. `42` or `{ foo: "bar" }`. Passing in a [matcher argument](#argument-matchers) will always take priority.
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
import { mock, when, It } from 'strong-mock';
|
|
372
|
+
|
|
373
|
+
// Use strict equality instead of deep equality.
|
|
374
|
+
const fn = mock<(x: number[]) => boolean>({ concreteMatcher: It.is });
|
|
331
375
|
when(() => fn([1, 2, 3])).thenReturn(true);
|
|
332
376
|
|
|
333
377
|
fn([1, 2, 3]); // throws because different arrays
|
|
334
378
|
```
|
|
335
379
|
|
|
380
|
+
#### Defaults
|
|
381
|
+
|
|
382
|
+
Mock options can be set for all mocks with `setDefaults`.
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
import { mock, when, setDefaults, Strictness } from 'strong-mock';
|
|
386
|
+
|
|
387
|
+
setDefaults({
|
|
388
|
+
strictness: Strictness.SUPER_STRICT
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Uses the new default.
|
|
392
|
+
const superStrictMock = mock<() => void>();
|
|
393
|
+
// Overrides the default.
|
|
394
|
+
const strictMock = mock<() => void>({ strictness: Strictness.STRICT });
|
|
395
|
+
```
|
|
396
|
+
|
|
336
397
|
## FAQ
|
|
337
398
|
|
|
338
399
|
### Why do I have to set all expectations first?
|
|
@@ -343,9 +404,9 @@ This design decision has a few reasons behind it. First, it forces you to be awa
|
|
|
343
404
|
|
|
344
405
|
Secondly, it will highlight potential design problems such as violations of the SOLID principles. If you find yourself duplicating expectations between tests and passing dummy values to them because your test is not concerned with them then you might want to look into splitting the code to only depend on things it really needs.
|
|
345
406
|
|
|
346
|
-
### Can I mock an existing object/function?
|
|
407
|
+
### Can I partially mock an existing object/function?
|
|
347
408
|
|
|
348
|
-
No,
|
|
409
|
+
No, passing a concrete implementation to `mock()` will be the same as passing a type: all properties will be mocked, and you have to set expectations on the ones that will be accessed.
|
|
349
410
|
|
|
350
411
|
### How do I set expectations on setters?
|
|
351
412
|
|
|
@@ -373,54 +434,6 @@ console.log(foo.bar(23)); // 'called 23'
|
|
|
373
434
|
|
|
374
435
|
The function in `thenReturn()` will be type checked against the actual interface, so you can make sure you're passing in an implementation that makes sense. Moreover, refactoring the interface will also refactor the expectation (in a capable IDE).
|
|
375
436
|
|
|
376
|
-

|
|
377
|
-
|
|
378
|
-
### Why does accessing an unused method throw?
|
|
379
|
-
|
|
380
|
-
Any unexpected property access will throw an error, even if the property is a method, and you never call it. This can sometimes be inconvenient if your code e.g. destructures your mock and only calls parts of it inside your test.
|
|
381
|
-
|
|
382
|
-
```typescript
|
|
383
|
-
interface Foo {
|
|
384
|
-
bar: () => number;
|
|
385
|
-
baz: () => number;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function doFoo(foo: Foo, { callBaz }: { callBaz: boolean }) {
|
|
389
|
-
// Will throw here with unexpected access on `baz`.
|
|
390
|
-
const { bar, baz } = foo;
|
|
391
|
-
|
|
392
|
-
bar();
|
|
393
|
-
|
|
394
|
-
if (callBaz) {
|
|
395
|
-
baz();
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
const foo = mock<Foo>();
|
|
400
|
-
when(() => foo.bar()).thenReturn(42);
|
|
401
|
-
|
|
402
|
-
// Throws with unexpected access on `baz`.
|
|
403
|
-
doFoo(foo, { callBaz: false });
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
To work around this, either change your code to avoid destructuring
|
|
407
|
-
|
|
408
|
-
```typescript
|
|
409
|
-
function doFoo(foo: Foo, callBaz: boolean) {
|
|
410
|
-
foo.bar();
|
|
411
|
-
|
|
412
|
-
if (callBaz) {
|
|
413
|
-
foo.baz();
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
or set a dummy expectation on the methods you're not interested in during the test.
|
|
419
|
-
|
|
420
|
-
```typescript
|
|
421
|
-
when(() => foo.baz()).thenThrow('should not be called').anyTimes();
|
|
422
|
-
```
|
|
423
|
-
|
|
424
437
|
### Can I spread/enumerate a mock?
|
|
425
438
|
|
|
426
439
|
Yes, and you will only get the properties that have expectations on them.
|
|
@@ -437,7 +450,6 @@ console.log(foo2.bar); // 42
|
|
|
437
450
|
console.log(foo2.baz); // undefined
|
|
438
451
|
```
|
|
439
452
|
|
|
440
|
-
|
|
441
453
|
### How can I ignore `undefined` keys when setting expectations on objects?
|
|
442
454
|
|
|
443
455
|
Use the `It.deepEquals` matcher explicitly inside `when` and pass `{ strict: false }`:
|
|
@@ -445,15 +457,17 @@ Use the `It.deepEquals` matcher explicitly inside `when` and pass `{ strict: fal
|
|
|
445
457
|
```ts
|
|
446
458
|
const fn = mock<(x: { foo: string }) => boolean>();
|
|
447
459
|
|
|
448
|
-
when(() => fn(
|
|
460
|
+
when(() => fn(
|
|
461
|
+
It.deepEquals({ foo: "bar" }, { strict: false }))
|
|
462
|
+
).thenReturn(true);
|
|
449
463
|
|
|
450
464
|
fn({ foo: "bar", baz: undefined }) === true
|
|
451
465
|
```
|
|
452
466
|
|
|
453
|
-
You can
|
|
467
|
+
You can set this behavior to be the default by configuring the [concrete matcher](#concrete-matcher), and set it on all mocks using [setDefaults](#defaults):
|
|
454
468
|
|
|
455
469
|
```ts
|
|
456
470
|
setDefaults({
|
|
457
|
-
|
|
471
|
+
concreteMatcher: (expected) => It.deepEquals(expected, { strict: false })
|
|
458
472
|
});
|
|
459
473
|
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Expectation, ReturnValue } from '../expectation';
|
|
2
1
|
import { Property } from '../../proxy';
|
|
2
|
+
import { Expectation, ReturnValue } from '../expectation';
|
|
3
3
|
export declare type Call = {
|
|
4
4
|
arguments: any[] | undefined;
|
|
5
5
|
};
|
|
@@ -31,7 +31,7 @@ export interface ExpectationRepository {
|
|
|
31
31
|
/**
|
|
32
32
|
* Get a return value for the given property.
|
|
33
33
|
*
|
|
34
|
-
* The value might be a non-callable e.g. a number or a string or it might
|
|
34
|
+
* The value might be a non-callable e.g. a number or a string, or it might
|
|
35
35
|
* be a function that, upon receiving arguments, will start a new search and
|
|
36
36
|
* return a value again.
|
|
37
37
|
*
|
|
@@ -52,6 +52,21 @@ export interface ExpectationRepository {
|
|
|
52
52
|
* get('getData').value(1, 2, '3', false, NaN) === 42
|
|
53
53
|
*/
|
|
54
54
|
get(property: Property): ReturnValue;
|
|
55
|
+
/**
|
|
56
|
+
* Get a return value for a function call.
|
|
57
|
+
*
|
|
58
|
+
* Note: this will only be invoked if the mocked type is a function. For
|
|
59
|
+
* method property calls {@link get} will be called instead.
|
|
60
|
+
*
|
|
61
|
+
* The list of expectations should be consulted from first to last when
|
|
62
|
+
* getting a return value. If none of them match it is up to the
|
|
63
|
+
* implementation to decide what to do.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* add(new Expectation(ApplyProp, [1, 2], 23);
|
|
67
|
+
* apply(1, 2) === 23
|
|
68
|
+
*/
|
|
69
|
+
apply(args: unknown[]): unknown;
|
|
55
70
|
/**
|
|
56
71
|
* Get all the properties that have expectations.
|
|
57
72
|
*
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Strictness } from '../../mock/options';
|
|
2
|
+
import { Property } from '../../proxy';
|
|
3
|
+
import { Expectation } from '../expectation';
|
|
4
|
+
import { CallMap, ExpectationRepository } from './expectation-repository';
|
|
5
|
+
declare type CountableExpectation = {
|
|
6
|
+
expectation: Expectation;
|
|
7
|
+
matchCount: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* An expectation repository for configurable levels of strictness.
|
|
11
|
+
*/
|
|
12
|
+
export declare class FlexibleRepository implements ExpectationRepository {
|
|
13
|
+
private strictness;
|
|
14
|
+
constructor(strictness?: Strictness);
|
|
15
|
+
protected readonly expectations: Map<Property, CountableExpectation[]>;
|
|
16
|
+
private readonly expectedCallStats;
|
|
17
|
+
private readonly unexpectedCallStats;
|
|
18
|
+
add(expectation: Expectation): void;
|
|
19
|
+
clear(): void;
|
|
20
|
+
apply: (args: unknown[]) => unknown;
|
|
21
|
+
get(property: Property): any;
|
|
22
|
+
private handlePropertyWithMatchingExpectations;
|
|
23
|
+
private handlePropertyWithNoExpectations;
|
|
24
|
+
getAllProperties(): Property[];
|
|
25
|
+
getCallStats(): {
|
|
26
|
+
expected: CallMap;
|
|
27
|
+
unexpected: CallMap;
|
|
28
|
+
};
|
|
29
|
+
getUnmet(): Expectation[];
|
|
30
|
+
private recordExpected;
|
|
31
|
+
private recordUnexpected;
|
|
32
|
+
private countAndConsume;
|
|
33
|
+
private consumeExpectation;
|
|
34
|
+
private getValueForUnexpectedCall;
|
|
35
|
+
private getValueForUnexpectedAccess;
|
|
36
|
+
}
|
|
37
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -5,4 +5,5 @@ export { verify, verifyAll } from './verify/verify';
|
|
|
5
5
|
export { It } from './expectation/it';
|
|
6
6
|
export { setDefaults } from './mock/defaults';
|
|
7
7
|
export type { Matcher } from './expectation/matcher';
|
|
8
|
-
export type {
|
|
8
|
+
export type { MockOptions } from './mock/options';
|
|
9
|
+
export { Strictness } from './mock/options';
|