0
# Testing Support
1
2
Testing utilities for unit testing reactive code with virtual time and marble testing.
3
4
## Capabilities
5
6
### Test Scheduler
7
8
Virtual time scheduler for deterministic testing of time-based operations.
9
10
```javascript { .api }
11
/**
12
* Creates a virtual time scheduler for testing
13
* @constructor
14
* @returns {TestScheduler} New test scheduler instance
15
*/
16
function TestScheduler();
17
18
/**
19
* Creates a hot observable for testing
20
* @param {...Object} records - Array of recorded events
21
* @returns {Observable} Hot observable with specified events
22
*/
23
testScheduler.createHotObservable = function(...records);
24
25
/**
26
* Creates a cold observable for testing
27
* @param {...Object} records - Array of recorded events
28
* @returns {Observable} Cold observable with specified events
29
*/
30
testScheduler.createColdObservable = function(...records);
31
32
/**
33
* Creates a test observer
34
* @returns {Observer} Test observer that records events
35
*/
36
testScheduler.createObserver = function();
37
38
/**
39
* Starts a test with specified timing
40
* @param {function} create - Function that creates the observable to test
41
* @param {number} [created] - Time when observable is created (default: 100)
42
* @param {number} [subscribed] - Time when subscription occurs (default: 200)
43
* @param {number} [disposed] - Time when subscription is disposed (default: 1000)
44
* @returns {Object} Test result with messages and subscriptions
45
*/
46
testScheduler.start = function(create, created, subscribed, disposed);
47
```
48
49
### Reactive Test Utilities
50
51
Static utilities for creating test records and timing.
52
53
```javascript { .api }
54
/**
55
* Default timing constants for tests
56
*/
57
Rx.ReactiveTest.created = 100;
58
Rx.ReactiveTest.subscribed = 200;
59
Rx.ReactiveTest.disposed = 1000;
60
61
/**
62
* Creates an OnNext test record
63
* @param {number} ticks - Time when value is emitted
64
* @param {*} value - Value to emit
65
* @returns {Object} OnNext test record
66
*/
67
Rx.ReactiveTest.onNext = function(ticks, value);
68
69
/**
70
* Creates an OnError test record
71
* @param {number} ticks - Time when error occurs
72
* @param {*} error - Error to emit
73
* @returns {Object} OnError test record
74
*/
75
Rx.ReactiveTest.onError = function(ticks, error);
76
77
/**
78
* Creates an OnCompleted test record
79
* @param {number} ticks - Time when completion occurs
80
* @returns {Object} OnCompleted test record
81
*/
82
Rx.ReactiveTest.onCompleted = function(ticks);
83
84
/**
85
* Creates a subscription record
86
* @param {number} subscribe - Time when subscription starts
87
* @param {number} [unsubscribe] - Time when subscription ends
88
* @returns {Object} Subscription test record
89
*/
90
Rx.ReactiveTest.subscribe = function(subscribe, unsubscribe);
91
```
92
93
### Basic Testing Example
94
95
```javascript
96
// Example test using TestScheduler
97
var scheduler = new Rx.TestScheduler();
98
99
// Create test data
100
var hotObservable = scheduler.createHotObservable(
101
Rx.ReactiveTest.onNext(150, 1), // Before subscription
102
Rx.ReactiveTest.onNext(210, 2), // After subscription
103
Rx.ReactiveTest.onNext(220, 3),
104
Rx.ReactiveTest.onCompleted(230)
105
);
106
107
var coldObservable = scheduler.createColdObservable(
108
Rx.ReactiveTest.onNext(10, 'a'),
109
Rx.ReactiveTest.onNext(20, 'b'),
110
Rx.ReactiveTest.onCompleted(30)
111
);
112
113
// Test the observable
114
var result = scheduler.start(function() {
115
return hotObservable.map(function(x) { return x * 2; });
116
});
117
118
// Assert results
119
assert.deepEqual(result.messages, [
120
Rx.ReactiveTest.onNext(210, 4), // 2 * 2
121
Rx.ReactiveTest.onNext(220, 6), // 3 * 2
122
Rx.ReactiveTest.onCompleted(230)
123
]);
124
```
125
126
### Advanced Testing Patterns
127
128
Testing time-based operations:
129
130
```javascript
131
var scheduler = new Rx.TestScheduler();
132
133
var result = scheduler.start(function() {
134
return Rx.Observable.interval(100, scheduler)
135
.take(3)
136
.map(function(x) { return x + 1; });
137
});
138
139
assert.deepEqual(result.messages, [
140
Rx.ReactiveTest.onNext(300, 1), // 200 + 100 interval
141
Rx.ReactiveTest.onNext(400, 2), // 200 + 200 interval
142
Rx.ReactiveTest.onNext(500, 3), // 200 + 300 interval
143
Rx.ReactiveTest.onCompleted(500)
144
]);
145
```
146
147
### Test Observer
148
149
Observers that record all events for assertion:
150
151
```javascript { .api }
152
/**
153
* Test observer that records all events
154
* @constructor
155
* @returns {TestObserver} Test observer instance
156
*/
157
function TestObserver();
158
159
// Properties available on test observer
160
testObserver.messages; // Array of recorded events
161
testObserver.subscriptions; // Array of subscription records
162
```
163
164
**Usage Example:**
165
166
```javascript
167
var scheduler = new Rx.TestScheduler();
168
var observer = scheduler.createObserver();
169
170
var source = scheduler.createHotObservable(
171
Rx.ReactiveTest.onNext(210, 1),
172
Rx.ReactiveTest.onNext(220, 2),
173
Rx.ReactiveTest.onCompleted(230)
174
);
175
176
source.subscribe(observer);
177
scheduler.start();
178
179
// Check recorded messages
180
console.log(observer.messages);
181
// Output: [onNext(210, 1), onNext(220, 2), onCompleted(230)]
182
```
183
184
### Marble Testing Concepts
185
186
While RxJS 4 doesn't have built-in marble testing, the patterns can be simulated:
187
188
```javascript
189
// Simulate marble testing with helper functions
190
function parseMarbles(marbles, values, scheduler) {
191
var result = [];
192
var time = 0;
193
194
for (var i = 0; i < marbles.length; i++) {
195
var char = marbles[i];
196
if (char === '-') {
197
time += 10; // Frame advance
198
} else if (char === '|') {
199
result.push(Rx.ReactiveTest.onCompleted(time));
200
} else if (char === 'x') {
201
result.push(Rx.ReactiveTest.onError(time, new Error('test error')));
202
} else if (values[char]) {
203
result.push(Rx.ReactiveTest.onNext(time, values[char]));
204
time += 10;
205
}
206
}
207
208
return scheduler.createColdObservable.apply(scheduler, result);
209
}
210
211
// Usage
212
var scheduler = new Rx.TestScheduler();
213
var source = parseMarbles('a-b-c|', {a: 1, b: 2, c: 3}, scheduler);
214
```
215
216
### Error Testing
217
218
Testing error scenarios:
219
220
```javascript
221
var scheduler = new Rx.TestScheduler();
222
223
var result = scheduler.start(function() {
224
return Rx.Observable.throw(new Error('test error'), scheduler);
225
});
226
227
assert.deepEqual(result.messages, [
228
Rx.ReactiveTest.onError(200, Error('test error'))
229
]);
230
```
231
232
### Subscription Testing
233
234
Testing subscription timing:
235
236
```javascript
237
var scheduler = new Rx.TestScheduler();
238
var source = scheduler.createHotObservable(/* events */);
239
240
var subscription = source.subscribe(observer);
241
scheduler.schedule(null, 250, function() {
242
subscription.dispose();
243
});
244
245
scheduler.start();
246
247
// Check subscription was disposed at correct time
248
assert.equal(observer.subscriptions[0].unsubscribe, 250);
249
```
250
251
### Recorded and Subscription Classes
252
253
Data structures for test records and subscription timing.
254
255
```javascript { .api }
256
/**
257
* Represents a recorded notification with timing
258
* @param {number} time - Time when notification occurred
259
* @param {Object} value - Notification object (OnNext, OnError, or OnCompleted)
260
* @returns {Recorded} Recorded notification
261
*/
262
function Recorded(time, value);
263
264
/**
265
* Represents a subscription lifetime
266
* @param {number} subscribe - Time when subscription started
267
* @param {number} [unsubscribe] - Time when subscription ended
268
* @returns {Subscription} Subscription record
269
*/
270
function Subscription(subscribe, unsubscribe);
271
```
272
273
### TestScheduler Advanced Methods
274
275
Additional methods for complex testing scenarios.
276
277
```javascript { .api }
278
/**
279
* Schedules an action to run at absolute time
280
* @param {*} state - State to pass to action
281
* @param {number} dueTime - Absolute time to run action
282
* @param {function} action - Action to execute
283
* @returns {Disposable} Disposable for canceling the action
284
*/
285
testScheduler.scheduleAbsolute = function(state, dueTime, action);
286
287
/**
288
* Schedules an action to run after relative time
289
* @param {*} state - State to pass to action
290
* @param {number} dueTime - Relative time to run action
291
* @param {function} action - Action to execute
292
* @returns {Disposable} Disposable for canceling the action
293
*/
294
testScheduler.scheduleRelative = function(state, dueTime, action);
295
296
/**
297
* Schedules a recursive action
298
* @param {*} state - State to pass to action
299
* @param {number} dueTime - Time to run action
300
* @param {function} action - Recursive action to execute
301
* @returns {Disposable} Disposable for canceling the action
302
*/
303
testScheduler.scheduleRecursiveAbsolute = function(state, dueTime, action);
304
305
/**
306
* Advances the scheduler clock to specified time
307
* @param {number} time - Time to advance to
308
*/
309
testScheduler.advanceTo = function(time);
310
311
/**
312
* Advances the scheduler clock by specified amount
313
* @param {number} time - Amount of time to advance
314
*/
315
testScheduler.advanceBy = function(time);
316
317
/**
318
* Starts the scheduler and runs all scheduled actions
319
*/
320
testScheduler.start = function();
321
322
/**
323
* Stops the scheduler
324
*/
325
testScheduler.stop = function();
326
327
/**
328
* Gets the current virtual time
329
* @returns {number} Current virtual time
330
*/
331
testScheduler.clock = number;
332
```
333
334
### Mock Observer
335
336
Observer implementation for testing that records all events.
337
338
```javascript { .api }
339
/**
340
* Creates a mock observer that records events
341
* @param {TestScheduler} scheduler - Test scheduler to use for timing
342
* @returns {MockObserver} Mock observer instance
343
*/
344
function MockObserver(scheduler);
345
346
// Mock observer properties
347
mockObserver.messages; // Array of recorded Recorded objects
348
mockObserver.isDisposed; // Whether observer is disposed
349
350
// Observer interface methods
351
mockObserver.onNext = function(value);
352
mockObserver.onError = function(error);
353
mockObserver.onCompleted = function();
354
```
355
356
### Virtual Time Scheduler
357
358
Base class for test scheduler with virtual time support.
359
360
```javascript { .api }
361
/**
362
* Base virtual time scheduler
363
* @param {number} [initialClock] - Initial virtual time
364
* @returns {VirtualTimeScheduler} Virtual time scheduler
365
*/
366
function VirtualTimeScheduler(initialClock);
367
368
// Virtual time scheduler methods
369
virtualScheduler.now = function(); // Current virtual time
370
virtualScheduler.scheduleAbsolute = function(state, dueTime, action);
371
virtualScheduler.scheduleRelative = function(state, dueTime, action);
372
virtualScheduler.start = function();
373
virtualScheduler.stop = function();
374
virtualScheduler.advanceTo = function(time);
375
virtualScheduler.advanceBy = function(time);
376
```
377
378
### Historical Scheduler
379
380
Scheduler for testing with historical time data.
381
382
```javascript { .api }
383
/**
384
* Creates a historical scheduler that compares against historical data
385
* @param {number} initialClock - Initial historical time
386
* @param {function} comparer - Function to compare historical data
387
* @returns {HistoricalScheduler} Historical scheduler
388
*/
389
function HistoricalScheduler(initialClock, comparer);
390
```
391
392
### Testing Best Practices
393
394
Key patterns for effective testing:
395
396
```javascript
397
// 1. Use consistent timing constants
398
var T = Rx.ReactiveTest;
399
var scheduler = new Rx.TestScheduler();
400
401
// 2. Test both hot and cold observables
402
var hot = scheduler.createHotObservable(
403
T.onNext(150, 1), // Before subscription
404
T.onNext(210, 2), // After subscription
405
T.onCompleted(250)
406
);
407
408
var cold = scheduler.createColdObservable(
409
T.onNext(10, 'a'), // Relative to subscription
410
T.onNext(20, 'b'),
411
T.onCompleted(30)
412
);
413
414
// 3. Test error scenarios
415
var errorSource = scheduler.createColdObservable(
416
T.onNext(10, 1),
417
T.onError(20, new Error('test'))
418
);
419
420
// 4. Test subscription lifecycle
421
var result = scheduler.start(
422
function() { return source.take(2); },
423
100, // Created
424
200, // Subscribed
425
300 // Disposed
426
);
427
428
// 5. Assert both values and timing
429
assert.deepEqual(result.messages, expectedMessages);
430
assert.deepEqual(source.subscriptions, expectedSubscriptions);
431
```