Memory leaks often don't show up in ordinary tests — they check values but not teardown. interval/socket/WebSocket can keep running after a component's destroy, and the test won't notice. A cleanup test explicitly proves: "after destroy, the stream is completed".
The problem it solves
HTTP requests complete on their own, so HTTP tests never leak. But interval, fromEvent, WebSocket, valueChanges are forever. Forget takeUntilDestroyed and a value-based test still passes while the leak remains.
Operators and why they matter
takeUntil(destroy$) — ties the source to the destroy signal. This is what we're testing.
finalize(() => log('teardown')) — gives us a way to assert that teardown happened.
interval — simulates a never-ending source in the test.
Gotchas
A value-only test ("received X ticks") does NOT prove cleanup. Without a finalize/teardown assert, leaks are invisible.
Don't leave an interval in a test without take or takeUntil. The suite hangs or fails with a timeout.
HTTP one-shot tests don't cover cleanup for long-lived streams. Different scenarios.
What you get
The test explicitly verifies: the stream completes on destroy and emits nothing more after. Leaks become visible.