0
# Runtime Mock Customization
1
2
Runtime mock instance customization with MockInstance for modifying behavior during test execution. Supports method stubbing, property overrides, and lifecycle management for fine-grained control over mock behavior.
3
4
## Capabilities
5
6
### MockInstance Function
7
8
Customizes mock instances at runtime with support for methods, properties, getters, and setters.
9
10
```typescript { .api }
11
/**
12
* Customizes a getter property on a mock class
13
* @param instance - Mock class to customize
14
* @param name - Property name to customize
15
* @param stub - Getter function implementation
16
* @param encapsulation - Must be 'get' for getter
17
* @returns The stub function
18
*/
19
function MockInstance<T extends object, K extends keyof T, S extends () => T[K]>(
20
instance: AnyType<T>,
21
name: K,
22
stub: S,
23
encapsulation: 'get'
24
): S;
25
26
/**
27
* Customizes a setter property on a mock class
28
* @param instance - Mock class to customize
29
* @param name - Property name to customize
30
* @param stub - Setter function implementation
31
* @param encapsulation - Must be 'set' for setter
32
* @returns The stub function
33
*/
34
function MockInstance<T extends object, K extends keyof T, S extends (value: T[K]) => void>(
35
instance: AnyType<T>,
36
name: K,
37
stub: S,
38
encapsulation: 'set'
39
): S;
40
41
/**
42
* Customizes a method or property on a mock class
43
* @param instance - Mock class to customize
44
* @param name - Method/property name to customize
45
* @param stub - Implementation function or value
46
* @returns The stub function or value
47
*/
48
function MockInstance<T extends object, K extends keyof T, S extends T[K]>(
49
instance: AnyType<T>,
50
name: K,
51
stub: S
52
): S;
53
54
/**
55
* Applies multiple customizations to a mock class
56
* @param instance - Mock class to customize
57
* @param overrides - Object with property overrides
58
* @returns The overrides object
59
*/
60
function MockInstance<T extends object>(
61
instance: AnyType<T>,
62
overrides: Partial<T>
63
): typeof overrides;
64
65
/**
66
* Sets up automatic initialization for mock instances
67
* @param instance - Mock class to customize
68
* @param init - Initialization function called when instance is created
69
* @returns The initialization function
70
*/
71
function MockInstance<T extends object>(
72
instance: AnyType<T>,
73
init?: (instance: T, injector: Injector) => Partial<T>
74
): typeof init;
75
```
76
77
**Usage Examples:**
78
79
```typescript
80
import { MockInstance } from "ng-mocks";
81
82
// Customize a method
83
MockInstance(AuthService, 'login', jasmine.createSpy('login')
84
.and.returnValue(of({ success: true })));
85
86
// Customize a property getter
87
MockInstance(ConfigService, 'apiUrl', () => 'http://test-api.com', 'get');
88
89
// Customize a property setter
90
MockInstance(UserService, 'currentUser', (user) => {
91
console.log('Setting user:', user);
92
}, 'set');
93
94
// Apply multiple customizations
95
MockInstance(DataService, {
96
getData: jasmine.createSpy('getData').and.returnValue(of([])),
97
isLoading: false,
98
errorMessage: null
99
});
100
101
// Automatic initialization
102
MockInstance(AuthService, (instance, injector) => ({
103
isAuthenticated: true,
104
user: { id: 1, name: 'Test User' },
105
login: jasmine.createSpy('login').and.returnValue(of({ success: true }))
106
}));
107
```
108
109
### MockReset Function
110
111
Resets MockInstance customizations for specified classes.
112
113
```typescript { .api }
114
/**
115
* Resets MockInstance customizations for one or more classes
116
* @param instances - Classes to reset MockInstance customizations for
117
*/
118
function MockReset(...instances: Array<AnyType<any>>): void;
119
```
120
121
**Usage Examples:**
122
123
```typescript
124
import { MockInstance, MockReset } from "ng-mocks";
125
126
// Set up customizations
127
MockInstance(AuthService, 'isAuthenticated', true);
128
MockInstance(DataService, 'getData', () => of(mockData));
129
130
// Reset specific services
131
MockReset(AuthService, DataService);
132
133
// In test cleanup
134
afterEach(() => {
135
MockReset(AuthService, DataService, ConfigService);
136
});
137
```
138
139
### Runtime Customization Patterns
140
141
Common patterns for using MockInstance effectively in tests.
142
143
**Service Method Stubbing:**
144
145
```typescript
146
import { MockInstance, MockReset } from "ng-mocks";
147
148
describe('UserComponent', () => {
149
beforeEach(() => {
150
// Customize service behavior for all tests
151
MockInstance(UserService, 'getUsers', jasmine.createSpy('getUsers')
152
.and.returnValue(of([
153
{ id: 1, name: 'John' },
154
{ id: 2, name: 'Jane' }
155
])));
156
157
MockInstance(AuthService, 'isAuthenticated', true);
158
});
159
160
afterEach(() => {
161
// Clean up customizations
162
MockReset(UserService, AuthService);
163
});
164
165
it('should load users on init', () => {
166
const fixture = MockRender(UserComponent);
167
const userService = ngMocks.get(UserService);
168
169
expect(userService.getUsers).toHaveBeenCalled();
170
});
171
});
172
```
173
174
**Property Access Control:**
175
176
```typescript
177
// Control property access
178
MockInstance(ConfigService, 'environment', () => 'test', 'get');
179
MockInstance(ConfigService, 'debug', (value) => {
180
console.log('Debug mode set to:', value);
181
}, 'set');
182
183
// Use in test
184
const config = ngMocks.get(ConfigService);
185
expect(config.environment).toBe('test');
186
config.debug = true; // Logs: "Debug mode set to: true"
187
```
188
189
**Dynamic Behavior Based on Test Context:**
190
191
```typescript
192
describe('Component with different service states', () => {
193
beforeEach(() => {
194
MockInstance(DataService, (instance) => {
195
// Different behavior based on test context
196
if (jasmine.currentSpec.description.includes('loading')) {
197
return { isLoading: true, data: null };
198
} else if (jasmine.currentSpec.description.includes('error')) {
199
return {
200
isLoading: false,
201
data: null,
202
error: 'Network error'
203
};
204
} else {
205
return {
206
isLoading: false,
207
data: [{ id: 1, name: 'Test' }],
208
error: null
209
};
210
}
211
});
212
});
213
214
it('should show loading state', () => {
215
const fixture = MockRender(MyComponent);
216
const service = ngMocks.get(DataService);
217
expect(service.isLoading).toBe(true);
218
});
219
220
it('should show error state', () => {
221
const fixture = MockRender(MyComponent);
222
const service = ngMocks.get(DataService);
223
expect(service.error).toBe('Network error');
224
});
225
});
226
```
227
228
**Chained Method Calls:**
229
230
```typescript
231
// Mock fluent interface
232
MockInstance(QueryBuilder, 'where', function(this: any, condition: string) {
233
this.conditions = this.conditions || [];
234
this.conditions.push(condition);
235
return this;
236
});
237
238
MockInstance(QueryBuilder, 'orderBy', function(this: any, field: string) {
239
this.orderField = field;
240
return this;
241
});
242
243
MockInstance(QueryBuilder, 'execute', function(this: any) {
244
return of({
245
conditions: this.conditions,
246
orderField: this.orderField
247
});
248
});
249
250
// Use in test
251
const queryBuilder = ngMocks.get(QueryBuilder);
252
const result = queryBuilder
253
.where('active = true')
254
.where('role = "admin"')
255
.orderBy('name')
256
.execute();
257
```
258
259
**Lifecycle Management:**
260
261
```typescript
262
describe('Component lifecycle', () => {
263
let serviceSpies: { [key: string]: jasmine.Spy };
264
265
beforeEach(() => {
266
serviceSpies = {
267
init: jasmine.createSpy('init'),
268
destroy: jasmine.createSpy('destroy'),
269
load: jasmine.createSpy('load').and.returnValue(of({}))
270
};
271
272
MockInstance(LifecycleService, serviceSpies);
273
});
274
275
afterEach(() => {
276
MockReset(LifecycleService);
277
});
278
279
it('should call init on component creation', () => {
280
const fixture = MockRender(MyComponent);
281
expect(serviceSpies.init).toHaveBeenCalled();
282
});
283
284
it('should call destroy on component destruction', () => {
285
const fixture = MockRender(MyComponent);
286
fixture.destroy();
287
expect(serviceSpies.destroy).toHaveBeenCalled();
288
});
289
});
290
```
291
292
**Integration with MockBuilder:**
293
294
```typescript
295
// Combine MockInstance with MockBuilder
296
describe('Integration test', () => {
297
beforeEach(() => {
298
// Set up MockInstance customizations
299
MockInstance(AuthService, 'user', { id: 1, role: 'admin' });
300
MockInstance(ConfigService, 'features', ['feature1', 'feature2']);
301
302
// Configure test environment
303
return MockBuilder(MyComponent, MyModule)
304
.keep(AuthService) // Keep real AuthService, but with MockInstance customizations
305
.mock(HttpClient)
306
.build();
307
});
308
309
afterEach(() => {
310
MockReset(AuthService, ConfigService);
311
});
312
313
it('should work with customized services', () => {
314
const fixture = MockRender(MyComponent);
315
const auth = ngMocks.get(AuthService);
316
317
expect(auth.user.role).toBe('admin');
318
});
319
});
320
```
321
322
### Advanced Customization Techniques
323
324
**Conditional Stubbing:**
325
326
```typescript
327
MockInstance(ApiService, 'request', (url: string) => {
328
if (url.includes('/users')) {
329
return of({ users: [{ id: 1, name: 'Test' }] });
330
} else if (url.includes('/error')) {
331
return throwError({ status: 500, message: 'Server Error' });
332
} else {
333
return of({ data: 'default' });
334
}
335
});
336
```
337
338
**State-Dependent Behavior:**
339
340
```typescript
341
let serviceState = { authenticated: false, user: null };
342
343
MockInstance(AuthService, 'isAuthenticated', () => serviceState.authenticated, 'get');
344
MockInstance(AuthService, 'user', () => serviceState.user, 'get');
345
MockInstance(AuthService, 'login', (credentials: any) => {
346
serviceState.authenticated = true;
347
serviceState.user = { id: 1, name: 'Test User' };
348
return of({ success: true });
349
});
350
MockInstance(AuthService, 'logout', () => {
351
serviceState.authenticated = false;
352
serviceState.user = null;
353
return of({ success: true });
354
});
355
```
356
357
**Spy Integration:**
358
359
```typescript
360
// Create comprehensive spy setup
361
const createServiceSpy = (methodName: string) => {
362
return jasmine.createSpy(methodName).and.callThrough();
363
};
364
365
MockInstance(DataService, {
366
load: createServiceSpy('load').and.returnValue(of([])),
367
save: createServiceSpy('save').and.returnValue(of({ success: true })),
368
delete: createServiceSpy('delete').and.returnValue(of({ success: true }))
369
});
370
```