0
# Test Doubles and Mocking
1
2
Complete Sinon.js integration for spies, stubs, and mocks with enhanced TypeScript experience and improved stubbing utilities.
3
4
## Capabilities
5
6
### Sinon Integration
7
8
Complete Sinon.js library re-export with full TypeScript support.
9
10
```typescript { .api }
11
/**
12
* Complete Sinon.js library for creating test doubles
13
* Includes spies, stubs, mocks, and fake timers
14
*/
15
const sinon: sinon.SinonStatic;
16
17
/**
18
* Sinon spy type definition for TypeScript
19
*/
20
type SinonSpy = sinon.SinonSpy;
21
```
22
23
**Usage Examples:**
24
25
```typescript
26
import { sinon } from "@loopback/testlab";
27
28
// Creating spies
29
const spy = sinon.spy();
30
const objectSpy = sinon.spy(console, 'log');
31
32
// Creating stubs
33
const stub = sinon.stub();
34
stub.returns("mocked value");
35
stub.withArgs("specific").returns("specific response");
36
37
// Creating mocks
38
const mock = sinon.mock(console);
39
mock.expects("log").once().withArgs("Hello");
40
41
// Fake timers
42
const clock = sinon.useFakeTimers();
43
clock.tick(1000);
44
clock.restore();
45
```
46
47
### Enhanced Stub Instance Creation
48
49
Improved stub instance creation with better TypeScript support and stubs accessor.
50
51
```typescript { .api }
52
/**
53
* Creates a new object with the given functions as the prototype and stubs all
54
* implemented functions with enhanced TypeScript support
55
* @param constructor - Object or class to stub
56
* @returns A stubbed version with stubs accessor
57
*/
58
function createStubInstance<TType extends object>(
59
constructor: sinon.StubbableType<TType>
60
): StubbedInstanceWithSinonAccessor<TType>;
61
62
/**
63
* Type for stubbed instances with additional stubs accessor
64
* Provides both the stubbed interface and access to Sinon stub methods
65
*/
66
type StubbedInstanceWithSinonAccessor<T> = T & {
67
stubs: sinon.SinonStubbedInstance<T>;
68
};
69
```
70
71
**Usage Examples:**
72
73
```typescript
74
import { createStubInstance } from "@loopback/testlab";
75
76
class UserService {
77
async findUser(id: string): Promise<User> {
78
// Implementation
79
}
80
81
async createUser(data: UserData): Promise<User> {
82
// Implementation
83
}
84
}
85
86
// Create stubbed instance
87
const userServiceStub = createStubInstance(UserService);
88
89
// Configure stubs - both approaches work
90
userServiceStub.findUser.resolves({id: "123", name: "Alice"});
91
userServiceStub.stubs.findUser.resolves({id: "123", name: "Alice"});
92
93
// Use as normal instance
94
const user = await userServiceStub.findUser("123");
95
96
// Access stub methods for verification
97
expect(userServiceStub.stubs.findUser).to.have.been.calledWith("123");
98
```
99
100
### Spy Capabilities
101
102
Comprehensive spy functionality for monitoring function calls.
103
104
```typescript { .api }
105
// Spy creation methods (from Sinon)
106
sinon.spy(): sinon.SinonSpy;
107
sinon.spy(target: any, property: string): sinon.SinonSpy;
108
sinon.spy(func: Function): sinon.SinonSpy;
109
110
// Spy interface
111
interface SinonSpy {
112
(): any;
113
called: boolean;
114
callCount: number;
115
calledOnce: boolean;
116
calledTwice: boolean;
117
calledThrice: boolean;
118
firstCall: sinon.SinonSpyCall;
119
secondCall: sinon.SinonSpyCall;
120
thirdCall: sinon.SinonSpyCall;
121
lastCall: sinon.SinonSpyCall;
122
calledBefore(anotherSpy: sinon.SinonSpy): boolean;
123
calledAfter(anotherSpy: sinon.SinonSpy): boolean;
124
calledOn(obj: any): boolean;
125
alwaysCalledOn(obj: any): boolean;
126
calledWith(...args: any[]): boolean;
127
alwaysCalledWith(...args: any[]): boolean;
128
calledWithExactly(...args: any[]): boolean;
129
alwaysCalledWithExactly(...args: any[]): boolean;
130
neverCalledWith(...args: any[]): boolean;
131
threw(): boolean;
132
threw(exception: string | Function): boolean;
133
alwaysThrew(): boolean;
134
alwaysThrew(exception: string | Function): boolean;
135
returned(value: any): boolean;
136
alwaysReturned(value: any): boolean;
137
restore(): void;
138
reset(): void;
139
getCalls(): sinon.SinonSpyCall[];
140
}
141
```
142
143
### Stub Capabilities
144
145
Advanced stubbing functionality with method chaining and conditional responses.
146
147
```typescript { .api }
148
// Stub creation methods (from Sinon)
149
sinon.stub(): sinon.SinonStub;
150
sinon.stub(obj: any): sinon.SinonStubbedInstance<any>;
151
sinon.stub(obj: any, property: string): sinon.SinonStub;
152
153
// Stub interface (extends SinonSpy)
154
interface SinonStub extends SinonSpy {
155
returns(value: any): sinon.SinonStub;
156
returnsArg(index: number): sinon.SinonStub;
157
returnsThis(): sinon.SinonStub;
158
resolves(value?: any): sinon.SinonStub;
159
rejects(error?: any): sinon.SinonStub;
160
throws(error?: Error | string): sinon.SinonStub;
161
callsArg(index: number): sinon.SinonStub;
162
callsArgWith(index: number, ...args: any[]): sinon.SinonStub;
163
yields(...args: any[]): sinon.SinonStub;
164
yieldsTo(property: string, ...args: any[]): sinon.SinonStub;
165
withArgs(...args: any[]): sinon.SinonStub;
166
onCall(n: number): sinon.SinonStub;
167
onFirstCall(): sinon.SinonStub;
168
onSecondCall(): sinon.SinonStub;
169
onThirdCall(): sinon.SinonStub;
170
}
171
```
172
173
**Usage Examples:**
174
175
```typescript
176
import { sinon, expect } from "@loopback/testlab";
177
178
// Basic stubbing
179
const stub = sinon.stub();
180
stub.returns("default");
181
stub.withArgs("special").returns("special case");
182
183
expect(stub()).to.equal("default");
184
expect(stub("special")).to.equal("special case");
185
186
// Promise stubbing
187
const asyncStub = sinon.stub();
188
asyncStub.resolves("success");
189
asyncStub.withArgs("error").rejects(new Error("failed"));
190
191
const result = await asyncStub();
192
expect(result).to.equal("success");
193
194
// Object method stubbing
195
const obj = { method: () => "original" };
196
const methodStub = sinon.stub(obj, "method");
197
methodStub.returns("stubbed");
198
199
expect(obj.method()).to.equal("stubbed");
200
methodStub.restore();
201
```
202
203
### Mock Capabilities
204
205
Mock objects with expectations and verification.
206
207
```typescript { .api }
208
// Mock creation (from Sinon)
209
sinon.mock(obj: any): sinon.SinonMock;
210
211
// Mock interface
212
interface SinonMock {
213
expects(method: string): sinon.SinonExpectation;
214
restore(): void;
215
verify(): void;
216
}
217
218
// Expectation interface
219
interface SinonExpectation {
220
atLeast(n: number): sinon.SinonExpectation;
221
atMost(n: number): sinon.SinonExpectation;
222
never(): sinon.SinonExpectation;
223
once(): sinon.SinonExpectation;
224
twice(): sinon.SinonExpectation;
225
thrice(): sinon.SinonExpectation;
226
exactly(n: number): sinon.SinonExpectation;
227
withArgs(...args: any[]): sinon.SinonExpectation;
228
withExactArgs(...args: any[]): sinon.SinonExpectation;
229
returns(value: any): sinon.SinonExpectation;
230
throws(error?: Error | string): sinon.SinonExpectation;
231
}
232
```
233
234
**Usage Examples:**
235
236
```typescript
237
import { sinon } from "@loopback/testlab";
238
239
const logger = { log: (msg: string) => console.log(msg) };
240
241
// Create mock with expectations
242
const mock = sinon.mock(logger);
243
mock.expects("log").once().withArgs("Hello World");
244
245
// Use the mocked object
246
logger.log("Hello World");
247
248
// Verify expectations
249
mock.verify(); // Throws if expectations not met
250
mock.restore();
251
```
252
253
### Fake Timers
254
255
Control time and async behavior in tests.
256
257
```typescript { .api }
258
// Timer methods (from Sinon)
259
sinon.useFakeTimers(): sinon.SinonFakeTimers;
260
sinon.useFakeTimers(config: Partial<sinon.SinonFakeTimersConfig>): sinon.SinonFakeTimers;
261
262
interface SinonFakeTimers {
263
tick(ms: number): void;
264
next(): void;
265
runAll(): void;
266
restore(): void;
267
reset(): void;
268
}
269
```
270
271
**Usage Examples:**
272
273
```typescript
274
import { sinon, expect } from "@loopback/testlab";
275
276
// Test with fake timers
277
const clock = sinon.useFakeTimers();
278
let called = false;
279
280
setTimeout(() => { called = true; }, 1000);
281
282
expect(called).to.be.false();
283
clock.tick(1000);
284
expect(called).to.be.true();
285
286
clock.restore();
287
```