|
1 | | -[`@fluffy-spoon/substitute`](https://www.npmjs.com/package/@fluffy-spoon/substitute) is a TypeScript port of [NSubstitute](http://nsubstitute.github.io), which aims to provide a much more fluent mocking opportunity for strong-typed languages. |
2 | | - |
3 | | -# Installing |
4 | | -`npm install @fluffy-spoon/substitute --save-dev` |
5 | | - |
6 | | -# Requirements |
7 | | -* `TypeScript^3.0.0` |
8 | | - |
9 | | -# Usage |
10 | | -```typescript |
11 | | -import { Substitute, Arg } from '@fluffy-spoon/substitute'; |
12 | | - |
13 | | -interface Calculator { |
14 | | - add(a: number, b: number): number; |
15 | | -} |
16 | | - |
17 | | -//Create: |
18 | | -var calculator = Substitute.for<Calculator>(); |
19 | | - |
20 | | -//Set a return value: |
21 | | -calculator.add(1, 2).returns(3); |
22 | | - |
23 | | -//Check received calls: |
24 | | -calculator.received().add(1, Arg.any()); |
25 | | -calculator.didNotReceive().add(2, 2); |
26 | | -``` |
27 | | - |
28 | | -## Creating a mock |
29 | | -`var calculator = Substitute.for<Calculator>();` |
30 | | - |
31 | | -## Setting return types |
32 | | -See the example below. The same syntax also applies to properties and fields. |
33 | | - |
34 | | -```typescript |
35 | | -//single return type |
36 | | -calculator.add(1, 2).returns(4); |
37 | | -console.log(calculator.add(1, 2)); //prints 4 |
38 | | -console.log(calculator.add(1, 2)); //prints undefined |
39 | | - |
40 | | -//multiple return types in sequence |
41 | | -calculator.add(1, 2).returns(3, 7, 9); |
42 | | -console.log(calculator.add(1, 2)); //prints 3 |
43 | | -console.log(calculator.add(1, 2)); //prints 7 |
44 | | -console.log(calculator.add(1, 2)); //prints 9 |
45 | | -console.log(calculator.add(1, 2)); //prints undefined |
46 | | -``` |
47 | | - |
48 | | -## Argument matchers |
49 | | -There are several ways of matching arguments. The examples below also applies to properties and fields. |
50 | | - |
51 | | -```typescript |
52 | | -import { Arg } from '@fluffy-spoon/substitute'; |
53 | | - |
54 | | -//ignoring arguments |
55 | | -calculator.add(Arg.any(), 2).returns(10); |
56 | | -console.log(calculator.add(1337, 3)); //prints undefined since second argument doesn't match |
57 | | -console.log(calculator.add(1337, 2)); //prints 10 since second argument matches |
58 | | - |
59 | | -//received call with first arg 1 and second arg less than 0 |
60 | | -calculator.received().add(1, Arg.is(x => x < 0)); |
61 | | -``` |
62 | | - |
63 | | -# Benefits over other mocking libraries |
64 | | -- Easier-to-understand fluent syntax. |
65 | | -- No need to cast to `any` in certain places (for instance, when overriding read-only properties) due to the `myProperty.returns(...)` syntax. |
66 | | -- Doesn't weigh much. |
67 | | -- Produces very clean and descriptive error messages. Try it out - you'll love it. |
68 | | -- Doesn't rely on object instances - you can produce a strong-typed fake from nothing, ensuring that everything is mocked. |
| 1 | +[`@fluffy-spoon/substitute`](https://www.npmjs.com/package/@fluffy-spoon/substitute) is a TypeScript port of [NSubstitute](http://nsubstitute.github.io), which aims to provide a much more fluent mocking opportunity for strong-typed languages. |
| 2 | + |
| 3 | +# Installing |
| 4 | +`npm install @fluffy-spoon/substitute --save-dev` |
| 5 | + |
| 6 | +# Requirements |
| 7 | +* `TypeScript^3.0.0` |
| 8 | + |
| 9 | +# Usage |
| 10 | +```typescript |
| 11 | +import { Substitute, Arg } from '@fluffy-spoon/substitute'; |
| 12 | + |
| 13 | +interface Calculator { |
| 14 | + add(a: number, b: number): number; |
| 15 | + subtract(a: number, b: number): number; |
| 16 | + divide(a: number, b: number): number; |
| 17 | +} |
| 18 | + |
| 19 | +//Create: |
| 20 | +var calculator = Substitute.for<Calculator>(); |
| 21 | + |
| 22 | +//Set a return value: |
| 23 | +calculator.add(1, 2).returns(3); |
| 24 | + |
| 25 | +//Check received calls: |
| 26 | +calculator.received().add(1, Arg.any()); |
| 27 | +calculator.didNotReceive().add(2, 2); |
| 28 | +``` |
| 29 | + |
| 30 | +## Creating a mock |
| 31 | +`var calculator = Substitute.for<Calculator>();` |
| 32 | + |
| 33 | +## Setting return types |
| 34 | +See the example below. The same syntax also applies to properties and fields. |
| 35 | + |
| 36 | +```typescript |
| 37 | +//single return type |
| 38 | +calculator.add(1, 2).returns(4); |
| 39 | +console.log(calculator.add(1, 2)); //prints 4 |
| 40 | +console.log(calculator.add(1, 2)); //prints undefined |
| 41 | + |
| 42 | +//multiple return types in sequence |
| 43 | +calculator.add(1, 2).returns(3, 7, 9); |
| 44 | +console.log(calculator.add(1, 2)); //prints 3 |
| 45 | +console.log(calculator.add(1, 2)); //prints 7 |
| 46 | +console.log(calculator.add(1, 2)); //prints 9 |
| 47 | +console.log(calculator.add(1, 2)); //prints undefined |
| 48 | +``` |
| 49 | + |
| 50 | +## Argument matchers |
| 51 | +There are several ways of matching arguments. The examples below also applies to properties and fields. |
| 52 | + |
| 53 | +### Matching specific arguments |
| 54 | +```typescript |
| 55 | +import { Arg } from '@fluffy-spoon/substitute'; |
| 56 | + |
| 57 | +//ignoring first argument |
| 58 | +calculator.add(Arg.any(), 2).returns(10); |
| 59 | +console.log(calculator.add(1337, 3)); //prints undefined since second argument doesn't match |
| 60 | +console.log(calculator.add(1337, 2)); //prints 10 since second argument matches |
| 61 | + |
| 62 | +//received call with first arg 1 and second arg less than 0 |
| 63 | +calculator.received().add(1, Arg.is(x => x < 0)); |
| 64 | +``` |
| 65 | + |
| 66 | +### Ignoring all arguments |
| 67 | +```typescript |
| 68 | +//ignoring all arguments |
| 69 | +calculator.add(Arg.all()).returns(10); |
| 70 | +console.log(calculator.add(1, 3)); //prints 10 |
| 71 | +console.log(calculator.add(5, 2)); //prints 10 |
| 72 | +``` |
| 73 | + |
| 74 | +### Match order |
| 75 | +The order of argument matchers matters. The first matcher that matches will always be used. Below are two examples. |
| 76 | + |
| 77 | +```typescript |
| 78 | +calculator.add(Arg.all()).returns(10); |
| 79 | +calculator.add(1, 3).returns(1337); |
| 80 | +console.log(calculator.add(1, 3)); //prints 10 |
| 81 | +console.log(calculator.add(5, 2)); //prints 10 |
| 82 | +``` |
| 83 | + |
| 84 | +```typescript |
| 85 | +calculator.add(1, 3).returns(1337); |
| 86 | +calculator.add(Arg.all()).returns(10); |
| 87 | +console.log(calculator.add(1, 3)); //prints 1337 |
| 88 | +console.log(calculator.add(5, 2)); //prints 10 |
| 89 | +``` |
| 90 | + |
| 91 | +## Partial mocks |
| 92 | +With partial mocks you always start with a true substitute where everything is mocked and then opt-out of substitutions in certain scenarios. |
| 93 | + |
| 94 | +```typescript |
| 95 | +import { Substitute, Arg } from '@fluffy-spoon/substitute'; |
| 96 | + |
| 97 | +class RealCalculator implements Calculator { |
| 98 | + add(a: number, b: number) => a + b; |
| 99 | + subtract(a: number, b: number) => a - b; |
| 100 | +} |
| 101 | + |
| 102 | +var realCalculator = new RealCalculator(); |
| 103 | +var fakeCalculator = Substitute.for<Calculator>(); |
| 104 | + |
| 105 | +//let the subtract method always use the real method |
| 106 | +fakeCalculator.subtract(Arg.all()).mimicks(realCalculator.subtract); |
| 107 | +console.log(fakeCalculator.subtract(20, 10)); //prints 10 |
| 108 | +console.log(fakeCalculator.subtract(1, 2)); //prints 10 |
| 109 | + |
| 110 | +//for the add method, we only use the real method when the first arg is less than 10 |
| 111 | +//else, we always return 1337 |
| 112 | +fakeCalculator.add(Arg.is(x < 10), Arg.any()).mimicks(realCalculator.add); |
| 113 | +fakeCalculator.add(Arg.is(x >= 10), Arg.any()).returns(1337); |
| 114 | +console.log(fakeCalculator.add(5, 100)); //prints 105 via real method |
| 115 | +console.log(fakeCalculator.add(210, 7)); //prints 1337 via fake method |
| 116 | + |
| 117 | +//for the divide method, we only use the real method for explicit arguments |
| 118 | +fakeCalculator.divide(10, 2).mimicks(realCalculator.divide); |
| 119 | +fakeCalculator.divide(Arg.all()).returns(1338); |
| 120 | +console.log(fakeCalculator.divide(10, 5)); //prints 5 |
| 121 | +console.log(fakeCalculator.divide(9, 5)); //prints 1338 |
| 122 | +``` |
| 123 | + |
| 124 | +# Benefits over other mocking libraries |
| 125 | +- Easier-to-understand fluent syntax. |
| 126 | +- No need to cast to `any` in certain places (for instance, when overriding read-only properties) due to the `myProperty.returns(...)` syntax. |
| 127 | +- Doesn't weigh much. |
| 128 | +- Produces very clean and descriptive error messages. Try it out - you'll love it. |
| 129 | +- Doesn't rely on object instances - you can produce a strong-typed fake from nothing, ensuring that everything is mocked. |
0 commit comments