All quizzesHard
Marble Testing
Preview — 3 of 10 questions
What is the Observable contract (Rx Contract) that every Observable must honor?
javascript
// This is the valid emission sequence any Observable must follow:
// next → next → next → (complete | error) ← then NOTHING more
// Implementing a compliant Observable:
const compliant$ = new Observable<number>(subscriber => {
// May call next any number of times
subscriber.next(1);
subscriber.next(2);
// Must call exactly one of these:
subscriber.complete();
// OR: subscriber.error(new Error('oops'));
// These calls are IGNORED after complete/error (SafeSubscriber guards this):
subscriber.next(3); // ignored
subscriber.complete(); // ignored
subscriber.error('late'); // ignored
});
// RxJS's SafeSubscriber wraps user observers to enforce the contract:
// It sets an internal 'isStopped' flag after error/complete
// and ignores subsequent notifications
compliant$.subscribe({
next: v => console.log(v), // 1, 2
complete: () => console.log('done'), // "done"
// 3 is never logged — contract is enforced by SafeSubscriber
});AAn Observable may emit any number of values in any order, including after completion or error
BAn Observable must always emit at least one value before completing
CAn Observable must call next* zero or more times, followed by exactly one error OR one complete — never both, and no emissions after either terminal event
DAn Observable can only emit synchronously — async emissions require Subject
How does TestScheduler achieve time-travel in virtual time tests?
javascript
import { TestScheduler } from 'rxjs/testing';
const scheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
scheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => {
// Virtual time test — runs synchronously, no real waiting
const source$ = hot(' -a-b-c-d-e-|');
const sub1 = ' ^----! '; // subscribe at 0, unsub at 50ms
const sub2 = ' ^----! '; // subscribe at 60ms, unsub at 110ms
const expected1 = ' -a-b- ';
const expected2 = ' -d-e- ';
expectObservable(source$, sub1).toBe(expected1);
expectObservable(source$, sub2).toBe(expected2);
});
// Testing switchMap behavior:
scheduler.run(({ cold, hot, expectObservable }) => {
const source$ = hot(' -a--b-----c-| ');
const inner$ = cold(' --x-y-| ');
// ↑ inner for each outer value
const result = source$.pipe(
switchMap(() => inner$),
);
// When 'b' arrives, the inner Observable from 'a' is cancelled:
expectObservable(result).toBe(' ---x--x-y-x-y-|');
});AIt patches Date.now() and setTimeout globally during test execution
BIt runs the test in a Web Worker that runs at 1000x speed
CIt maintains a virtual clock and a priority queue of scheduled actions; flush() advances the clock synchronously, processing all scheduled work without real waiting
DIt uses JavaScript's performance.now() with a custom offset
When should you use effect() instead of toSignal(obs$.pipe(tap(...)))?
javascript
@Component({ standalone: true, changeDetection: ChangeDetectionStrategy.OnPush })
export class DashboardComponent {
private userService = inject(UserService);
private analyticsService = inject(AnalyticsService);
// toSignal — binds Observable data to a signal for the template
// ✅ Use for: displaying data, computed values
users = toSignal(this.userService.getUsers(), { initialValue: [] });
activeCount = computed(() => this.users().filter(u => u.active).length);
// effect — reacts to signal changes with side effects
// ✅ Use for: logging, analytics, DOM mutations, calling other services
constructor() {
effect(() => {
// Runs whenever activeCount() changes
this.analyticsService.track('active_users', { count: this.activeCount() });
});
}
}
// Contrast: toSignal for template binding
// ✅ Value available in template synchronously
template = `<p>Active: {{ activeCount() }}</p>`;
// Anti-pattern: using effect for data transformation
// ❌ DON'T do this — use computed() instead
effect(() => {
this.doubleCount = this.count() * 2; // write to signal in effect is a warning
});AUse effect() for side effects that should react to signal changes (logging, analytics, DOM updates); use toSignal() for binding Observable values to the template
BAlways use effect() — toSignal() doesn't support side effects
Ceffect() runs synchronously; toSignal() runs asynchronously — choose based on timing needs
DThere is no difference — effect() is built on toSignal() internally
Sign up free to play
Answer all 10 questions (7 more), see explanations for every answer, and track your score.