strong-mock 7.3.0 → 8.0.0-beta.0

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 CHANGED
@@ -6,7 +6,7 @@
6
6
  </div>
7
7
 
8
8
  ```typescript
9
- import { mock, when, instance } from 'strong-mock';
9
+ import { mock, when } from 'strong-mock';
10
10
 
11
11
  interface Foo {
12
12
  bar: (x: number) => string;
@@ -14,9 +14,9 @@ interface Foo {
14
14
 
15
15
  const foo = mock<Foo>();
16
16
 
17
- when(foo.bar(23)).thenReturn('I am strong!');
17
+ when(() => foo.bar(23)).thenReturn('I am strong!');
18
18
 
19
- console.log(instance(foo).bar(23)); // 'I am strong!'
19
+ console.log(foo.bar(23)); // 'I am strong!'
20
20
  ```
21
21
 
22
22
  ----
@@ -27,12 +27,12 @@ console.log(instance(foo).bar(23)); // 'I am strong!'
27
27
  <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
28
28
  **Table of Contents**
29
29
 
30
- - [Installation](#installation)
31
- - [Requirements](#requirements)
32
30
  - [Features](#features)
33
31
  - [Type safety](#type-safety)
34
32
  - [Useful error messages](#useful-error-messages)
35
33
  - [Type safe argument matchers](#type-safe-argument-matchers)
34
+ - [Installation](#installation)
35
+ - [Requirements](#requirements)
36
36
  - [API](#api)
37
37
  - [Setting expectations](#setting-expectations)
38
38
  - [Setting multiple expectations](#setting-multiple-expectations)
@@ -52,25 +52,11 @@ console.log(instance(foo).bar(23)); // 'I am strong!'
52
52
  - [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
53
  - [How do I provide a function for the mock to call?](#how-do-i-provide-a-function-for-the-mock-to-call)
54
54
  - [Why does accessing an unused method throw?](#why-does-accessing-an-unused-method-throw)
55
- - [Can I spread/enumerate a mock instance?](#can-i-spreadenumerate-a-mock-instance)
55
+ - [Can I spread/enumerate a mock?](#can-i-spreadenumerate-a-mock)
56
56
  - [How can I ignore `undefined` keys when setting expectations on objects?](#how-can-i-ignore-undefined-keys-when-setting-expectations-on-objects)
57
57
 
58
58
  <!-- END doctoc generated TOC please keep comment here to allow auto update -->
59
59
 
60
- ## Installation
61
-
62
- ```
63
- npm i -D strong-mock
64
- ```
65
-
66
- ```
67
- yarn add -D strong-mock
68
- ```
69
-
70
- ## Requirements
71
-
72
- strong-mock requires an environment that supports the [ES6 Proxy object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This is necessary to create dynamic mocks from types because TypeScript does not support reflection i.e. exposing the type info at runtime.
73
-
74
60
  ## Features
75
61
 
76
62
  ### Type safety
@@ -87,34 +73,44 @@ Error messages include the property that has been accessed, any arguments passed
87
73
 
88
74
  ### Type safe argument matchers
89
75
 
90
- ![type safe matchers](./media/type-safe-matchers.png)
76
+ Optional argument matchers allow you to create complex expectations, while still maintaining type safety.
91
77
 
92
- ## API
78
+ ![type safe matchers](./media/type-safe-matchers.png)
93
79
 
94
- ### Setting expectations
80
+ ## Installation
95
81
 
96
- Expectations are set by calling the mock inside a `when()` call and finishing it by setting a return value.
82
+ ```
83
+ npm i -D strong-mock
84
+ ```
97
85
 
98
- ```typescript
99
- when(foo.bar(23)).thenReturn('awesome');
100
86
  ```
87
+ yarn add -D strong-mock
88
+ ```
89
+
90
+ ## Requirements
91
+
92
+ strong-mock requires an environment that supports the [ES6 Proxy object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This is necessary to create dynamic mocks from types because TypeScript does not support reflection i.e. exposing the type info at runtime.
93
+
94
+ ## API
95
+
96
+ ### Setting expectations
101
97
 
102
- After expectations have been set you need to get an instance of the mock by calling `instance()`.
98
+ Expectations are set by calling the mock inside a `when` callback and finishing it by setting a return value.
103
99
 
104
100
  ```typescript
105
- instance(foo)
101
+ when(() => foo.bar(23)).thenReturn('awesome');
106
102
  ```
107
103
 
108
104
  ### Setting multiple expectations
109
105
 
110
- You can set as many expectations as you want by calling `when()` multiple times. If you have multiple expectations with the same arguments they will be consumed in the order they were created.
106
+ You can set as many expectations as you want by calling `when` multiple times. If you have multiple expectations with the same arguments they will be consumed in the order they were created.
111
107
 
112
108
  ```typescript
113
- when(foo.bar(23)).thenReturn('awesome');
114
- when(foo.bar(23)).thenReturn('even more awesome');
109
+ when(() => foo.bar(23)).thenReturn('awesome');
110
+ when(() => foo.bar(23)).thenReturn('even more awesome');
115
111
 
116
- console.log(instance(foo).bar(23)); // awesome
117
- console.log(instance(foo).bar(23)); // even more awesome
112
+ console.log(foo.bar(23)); // awesome
113
+ console.log(foo.bar(23)); // even more awesome
118
114
  ```
119
115
 
120
116
  By default, each call is expected to be called only once. You can expect a call to be made multiple times using the [invocation count](#setting-invocation-count-expectations) helpers.
@@ -126,12 +122,12 @@ You can expect a call to be made multiple times by using the invocation count he
126
122
  ```typescript
127
123
  const fn = mock<(x: number) => number>();
128
124
 
129
- when(fn(1)).thenReturn(1).between(2, 3);
125
+ when(() => fn(1)).thenReturn(1).between(2, 3);
130
126
 
131
- console.log(instance(fn)(1)); // 1
132
- console.log(instance(fn)(1)); // 1
133
- console.log(instance(fn)(1)); // 1
134
- console.log(instance(fn)(1)); // throws because the expectation is finished
127
+ console.log(fn(1)); // 1
128
+ console.log(fn(1)); // 1
129
+ console.log(fn(1)); // 1
130
+ console.log(fn(1)); // throws because the expectation is finished
135
131
  ```
136
132
 
137
133
  ### Mocking interfaces
@@ -146,11 +142,11 @@ interface Foo {
146
142
 
147
143
  const foo = mock<Foo>();
148
144
 
149
- when(foo.bar(23)).thenReturn('awesome');
150
- when(foo.baz).thenReturn(100);
145
+ when(() => foo.bar(23)).thenReturn('awesome');
146
+ when(() => foo.baz).thenReturn(100);
151
147
 
152
- console.log(instance(foo).bar(23)); // 'awesome'
153
- console.log(instance(foo).baz); // 100
148
+ console.log(foo.bar(23)); // 'awesome'
149
+ console.log(foo.baz); // 100
154
150
  ```
155
151
 
156
152
  Since the mock is type safe the compiler will guarantee that you're only mocking things that actually exist on the interface.
@@ -164,9 +160,9 @@ type Fn = (x: number) => number;
164
160
 
165
161
  const fn = mock<Fn>();
166
162
 
167
- when(fn(1)).thenReturn(2);
163
+ when(() => fn(1)).thenReturn(2);
168
164
 
169
- console.log(instance(fn)(1)); // 2
165
+ console.log(fn(1)); // 2
170
166
  ```
171
167
 
172
168
  ### Mocking promises
@@ -178,9 +174,9 @@ type Fn = (x: number) => Promise<number>;
178
174
 
179
175
  const fn = mock<Fn>();
180
176
 
181
- when(fn(1)).thenResolve(2);
177
+ when(() => fn(1)).thenResolve(2);
182
178
 
183
- console.log(await instance(fn)()); // 2
179
+ console.log(await fn()); // 2
184
180
  ```
185
181
 
186
182
  ### Throwing errors
@@ -192,8 +188,8 @@ type FnWithPromise = (x: number) => Promise<void>;
192
188
  const fn = mock<Fn>();
193
189
  const fnWithPromise = mock<FnWithPromise>();
194
190
 
195
- when(fn(1)).thenThrow();
196
- when(fnWithPromise(1)).thenReject();
191
+ when(() => fn(1)).thenThrow();
192
+ when(() => fnWithPromise(1)).thenReject();
197
193
  ```
198
194
 
199
195
  You'll notice there is no `never()` helper - if you expect a call to not be made simply don't set an expectation on it and the mock will throw if the call happens.
@@ -205,7 +201,7 @@ Calling `verify(mock)` will make sure that all expectations set on `mock` have b
205
201
  ```typescript
206
202
  const fn = mock<(x: number) => number>();
207
203
 
208
- when(fn(1)).thenReturn(1).between(2, 10);
204
+ when(() => fn(1)).thenReturn(1).between(2, 10);
209
205
 
210
206
  verify(fn); // throws
211
207
  ```
@@ -216,7 +212,7 @@ It will also throw if any unexpected calls happened that were maybe caught in th
216
212
  const fn = mock<() => void>();
217
213
 
218
214
  try {
219
- instance(fn)(); // throws because the call is unexpected
215
+ fn(); // throws because the call is unexpected
220
216
  } catch(e) {
221
217
  // your code might transition to an error state here
222
218
  }
@@ -224,7 +220,7 @@ try {
224
220
  verify(fn); // throws
225
221
  ```
226
222
 
227
- It is recommended that that you call `verify()` on your mocks at the end of every test. This will make sure you don't have any unused expectations in your tests and that your code hasn't silently caught any of the errors that are thrown when an unexpected call happens. You can use `verifyAll()` to check all existing mocks e.g. in an `afterEach` hook.
223
+ It is recommended that you call `verify()` on your mocks at the end of every test. This will make sure you don't have any unused expectations in your tests and that your code did not silently catch any of the errors that are thrown when an unexpected call happens. You can use `verifyAll()` to check all existing mocks e.g. in an `afterEach` hook.
228
224
 
229
225
  ![verify error](./media/verify.png)
230
226
 
@@ -235,11 +231,11 @@ You can remove all expectations from a mock by using the `reset()` method:
235
231
  ```typescript
236
232
  const fn = mock<(x: number) => number>();
237
233
 
238
- when(fn(1)).thenReturn(1);
234
+ when(() => fn(1)).thenReturn(1);
239
235
 
240
236
  reset(fn);
241
237
 
242
- instance(fn)(1); // throws
238
+ fn(1); // throws
243
239
  ```
244
240
 
245
241
  If you create common mocks that are shared by multiple tests you should reset them before using them e.g. in a `beforeEach` hook. You can use `resetAll()` to reset all existing mocks.
@@ -253,12 +249,12 @@ const fn = mock<
253
249
  (x: number, data: { values: number[]; labels: string[] }) => string
254
250
  >();
255
251
 
256
- when(fn(
252
+ when(() => fn(
257
253
  It.isAny(),
258
254
  It.isObject({ values: [1, 2, 3] })
259
255
  )).thenReturn('matched!');
260
256
 
261
- console.log(instance(fn)(
257
+ console.log(fn(
262
258
  123,
263
259
  { values: [1, 2, 3], labels: ['a', 'b', 'c'] })
264
260
  ); // 'matched!'
@@ -277,12 +273,12 @@ Available matchers:
277
273
 
278
274
  The following table illustrates the differences between the equality matchers:
279
275
 
280
- expected | actual | `It.is` | `It.deepEquals` | `It.deepEquals({ strict: false })`
281
- -------|----------|---------|-----------------|-----------------------------------
282
- `"foo"` | `"bar"` | equal | equal | equal
283
- `{ foo: "bar" }` | `{ foo: "bar" }` | not equal | equal | equal
284
- `{ }` | `{ foo: undefined }` | not equal | not equal | equal
285
- `new (class {})()` | `new (class {})()` | not equal | not equal | equal
276
+ | expected | actual | `It.is` | `It.deepEquals` | `It.deepEquals({ strict: false })` |
277
+ |--------------------|----------------------|-----------|-----------------|------------------------------------|
278
+ | `"foo"` | `"bar"` | equal | equal | equal |
279
+ | `{ foo: "bar" }` | `{ foo: "bar" }` | not equal | equal | equal |
280
+ | `{ }` | `{ foo: undefined }` | not equal | not equal | equal |
281
+ | `new (class {})()` | `new (class {})()` | not equal | not equal | equal |
286
282
 
287
283
  Some matchers, like `isObject` and `isArray` support nesting matchers:
288
284
 
@@ -299,9 +295,9 @@ You can create arbitrarily complex and type safe matchers with `It.matches(cb)`:
299
295
  ```typescript
300
296
  const fn = mock<(x: number, y: number[]) => string>();
301
297
 
302
- when(fn(
298
+ when(() => fn(
303
299
  It.matches(x => x > 0),
304
- It.matches(y => y.values.includes(42))
300
+ It.matches(y => y.includes(42))
305
301
  )).thenReturn('matched');
306
302
  ```
307
303
 
@@ -313,9 +309,9 @@ type Cb = (value: number) => number;
313
309
  const fn = mock<(cb: Cb) => number>();
314
310
 
315
311
  const matcher = It.willCapture<Cb>();
316
- when(fn(matcher)).thenReturn(42);
312
+ when(() => fn(matcher)).thenReturn(42);
317
313
 
318
- console.log(instance(fn)(23, (x) => x + 1)); // 42
314
+ console.log(fn(23, (x) => x + 1)); // 42
319
315
  console.log(matcher.value?.(3)); // 4
320
316
  ```
321
317
 
@@ -324,7 +320,7 @@ console.log(matcher.value?.(3)); // 4
324
320
  You can override the default matcher that will be used when setting expectations with non-matcher values e.g. `42` or `{ foo: "bar" }`.
325
321
 
326
322
  ```ts
327
- import { mock, when, instance, It, setDefaults } from 'strong-mock';
323
+ import { mock, when, It, setDefaults } from 'strong-mock';
328
324
 
329
325
  // Use strict equality instead of deep equality.
330
326
  setDefaults({
@@ -332,9 +328,9 @@ setDefaults({
332
328
  })
333
329
 
334
330
  const fn = mock<(x: number[]) => boolean>();
335
- when(fn([1, 2, 3])).thenReturn(true);
331
+ when(() => fn([1, 2, 3])).thenReturn(true);
336
332
 
337
- instance(fn)([1, 2, 3]); // throws because different arrays
333
+ fn([1, 2, 3]); // throws because different arrays
338
334
  ```
339
335
 
340
336
  ## FAQ
@@ -343,7 +339,7 @@ instance(fn)([1, 2, 3]); // throws because different arrays
343
339
 
344
340
  This library is different from other mocking/spying libraries you might have used before such as [sinon](https://sinonjs.org) or [jest](https://jestjs.io/docs/en/mock-functions). Whereas those libraries are focused on recording calls to the mocks and always returning something, strong-mock requires you to set your expectations upfront. If a call happens that is not expected the mock will throw an error.
345
341
 
346
- This design decision has a few reasons behind it. First of all, it forces you to be aware of what your code needs from its dependencies. Spying libraries encourage checking those needs at the end of the test after the code has already called the mocks. This can lead to tests missing dependency calls that just happen to not throw any error at runtime with the dummy values that the spies return.
342
+ This design decision has a few reasons behind it. First, it forces you to be aware of what your code needs from its dependencies. Spying libraries encourage checking those needs at the end of the test after the code has already called the mocks. This can lead to tests missing dependency calls that just happen to not throw any error at runtime with the dummy values that the spies return.
347
343
 
348
344
  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.
349
345
 
@@ -357,11 +353,11 @@ You currently can't do that. Please use a normal method instead e.g. `setFoo()`
357
353
 
358
354
  ### Why do I have to set a return value even if it's `undefined`?
359
355
 
360
- To make side effects explicit and to prevent future refactoring headaches. If you would have just `when(fn())` and you later changed `fn()` to return a `number` then your expectation would become incorrect and the compiler couldn't check that for you.
356
+ To make side effects explicit and to prevent future refactoring headaches. If you had just `when(() => fn())` and you later changed `fn()` to return a `number` then your expectation would become incorrect and the compiler couldn't check that for you.
361
357
 
362
358
  ### How do I provide a function for the mock to call?
363
359
 
364
- There is no `thenCall()` method because it can't be safely typed - the type for `thenReturn()` is inferred from the return type in `when()`, meaning that the required type would be the return value for the function, not the function itself. However, we can leverage this by setting an expectation on the function property instead:
360
+ There is no `thenCall()` method because it can't be safely typed - the type for `thenReturn()` is inferred from the return type in `when`, meaning that the required type would be the return value for the function, not the function itself. However, we can leverage this by setting an expectation on the function property instead:
365
361
 
366
362
  ```typescript
367
363
  interface Foo {
@@ -370,12 +366,12 @@ interface Foo {
370
366
 
371
367
  const foo = mock<Foo>();
372
368
 
373
- when(foo.bar).thenReturn(x => `called ${x}`);
369
+ when(() => foo.bar).thenReturn(x => `called ${x}`);
374
370
 
375
- console.log(instance(foo).bar(23)); // 'called 23'
371
+ console.log(foo.bar(23)); // 'called 23'
376
372
  ```
377
373
 
378
- 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).
374
+ 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).
379
375
 
380
376
  ![call-rename](media/rename-args.gif)
381
377
 
@@ -401,10 +397,10 @@ function doFoo(foo: Foo, { callBaz }: { callBaz: boolean }) {
401
397
  }
402
398
 
403
399
  const foo = mock<Foo>();
404
- when(foo.bar()).thenReturn(42);
400
+ when(() => foo.bar()).thenReturn(42);
405
401
 
406
402
  // Throws with unexpected access on `baz`.
407
- doFoo(instance(foo), { callBaz: false });
403
+ doFoo(foo, { callBaz: false });
408
404
  ```
409
405
 
410
406
  To work around this, either change your code to avoid destructuring
@@ -422,20 +418,20 @@ function doFoo(foo: Foo, callBaz: boolean) {
422
418
  or set a dummy expectation on the methods you're not interested in during the test.
423
419
 
424
420
  ```typescript
425
- when(foo.baz()).thenThrow('should not be called').anyTimes();
421
+ when(() => foo.baz()).thenThrow('should not be called').anyTimes();
426
422
  ```
427
423
 
428
- ### Can I spread/enumerate a mock instance?
424
+ ### Can I spread/enumerate a mock?
429
425
 
430
426
  Yes, and you will only get the properties that have expectations on them.
431
427
 
432
428
  ```typescript
433
429
  const foo = mock<{ bar: number; baz: number }>();
434
- when(foo.bar).thenReturn(42);
430
+ when(() => foo.bar).thenReturn(42);
435
431
 
436
- console.log(Object.keys(instance(foo))); // ['bar']
432
+ console.log(Object.keys(foo)); // ['bar']
437
433
 
438
- const foo2 = { ...instance(foo) };
434
+ const foo2 = { ...foo };
439
435
 
440
436
  console.log(foo2.bar); // 42
441
437
  console.log(foo2.baz); // undefined
@@ -449,9 +445,9 @@ Use the `It.deepEquals` matcher explicitly inside `when` and pass `{ strict: fal
449
445
  ```ts
450
446
  const fn = mock<(x: { foo: string }) => boolean>();
451
447
 
452
- when(fn(It.deepEquals({ foo: "bar" }, { strict: false }))).thenReturn(true);
448
+ when(() => fn(It.deepEquals({ foo: "bar" }, { strict: false }))).thenReturn(true);
453
449
 
454
- instance(fn)({ foo: "bar", baz: undefined }) === true
450
+ fn({ foo: "bar", baz: undefined }) === true
455
451
  ```
456
452
 
457
453
  You can also set this behavior to be the default by using [`setDefaults`](#overriding-default-matcher):
package/dist/errors.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Expectation } from './expectation/expectation';
2
2
  import { CallMap } from './expectation/repository/expectation-repository';
3
- import { PendingExpectation } from './when/pending-expectation';
4
3
  import { Property } from './proxy';
4
+ import { PendingExpectation } from './when/pending-expectation';
5
5
  export declare class UnfinishedExpectation extends Error {
6
6
  constructor(pendingExpectation: PendingExpectation);
7
7
  }
@@ -22,7 +22,7 @@ export declare const It: {
22
22
  containing?: string | undefined;
23
23
  }) => TypeMatcher<string>;
24
24
  isArray: <T_4 extends any[]>(containing?: T_4 | undefined) => TypeMatcher<T_4>;
25
- willCapture: <T_5 = unknown>(name?: string | undefined) => T_5 & Matcher & {
25
+ willCapture: <T_5 = unknown>(name?: string) => T_5 & Matcher & {
26
26
  value: T_5 | undefined;
27
27
  };
28
28
  };
@@ -1,6 +1,6 @@
1
+ import { Property } from '../../proxy';
1
2
  import { Expectation, ReturnValue } from '../expectation';
2
3
  import { CallMap, ExpectationRepository } from './expectation-repository';
3
- import { Property } from '../../proxy';
4
4
  export declare type CountableExpectation = {
5
5
  expectation: Expectation;
6
6
  matchCount: number;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export { mock } from './mock/mock';
2
2
  export { when } from './when/when';
3
- export { instance } from './instance/instance';
4
3
  export { reset, resetAll } from './verify/reset';
5
4
  export { verify, verifyAll } from './verify/verify';
6
5
  export { It } from './expectation/it';