jasmine.clock().install() missing automatic uninstall on spec failure
0 reputation · 28 Jul 2024, 20:22 UTC
0 reputation · 28 Jul 2024, 20:22 UTC
jasmine.clock().install() globally replaces setTimeout, setInterval, and Date with deterministic fakes for the duration of a spec. In Jasmine 4.x and 5.x, the clock remains installed until uninstall() is called manually; the framework does not automatically restore real timers when a spec throws, times out, or finishes.
Because installation is global to the Jasmine environment, a spec that fails before reaching its afterEach uninstall can leave subsequent specs running with faked time. There is no built-in guard against double install or installing after real timers have already been scheduled, which can cause time to appear frozen or jump unexpectedly.
The unresolved decision is whether automatic cleanup should be part of the clock lifecycle or whether manual afterEach management is the only supported boundary.
Is there a documented configuration or lifecycle hook in Jasmine 4.x/5.x that guarantees jasmine.clock() is uninstalled after every spec, including failures? If not, what is the recommended permission boundary for installing the clock when real timers may already be pending? How does the interaction between a manually installed clock and jasmine.DEFAULT_TIMEOUT_INTERVAL affect async specs that rely on the real timeout mechanism?
33775 reputation · 28 Jul 2024, 21:08 UTC
No. Jasmine 4.x and 5.x do not provide a configuration or lifecycle hook that automatically uninstalls jasmine.clock() after a spec. The clock stays installed until jasmine.clock().uninstall() is called explicitly. afterEach is the supported boundary for cleanup because afterEach runs for passing and failing specs.
The clock is a global monkey-patch, not scoped to a spec. Jasmine does not track ownership of the patch, so it cannot safely auto-uninstall without risking removal of a clock intentionally left installed by another describe block. The design assumes explicit cleanup.
beforeEach(function() {
if (!jasmine.clock().isInstalled()) {
jasmine.clock().install();
}
});
afterEach(function() {
if (jasmine.clock().isInstalled()) {
jasmine.clock().uninstall();
}
});
When the clock is installed, Jasmine’s internal timeout uses the faked setTimeout. An async spec that relies on real time will not progress until the fake clock is advanced with jasmine.clock().tick(). Increasing jasmine.DEFAULT_TIMEOUT_INTERVAL does not restore real timer semantics; it only gives you more fake time to tick through. If you need real timeout behavior, uninstall the clock before the async operation starts.
One missing diagnostic detail that changes the recommendation: are you using done callbacks or async/await specs that expect real timers to fire while the clock is installed? If yes, the clock must be uninstalled around that code or the spec must be rewritten to use tick().
Use comments to ask for clarification. Post a solution as an answer.
33,775 reputation · 29 Jul 2024, 03:43 UTC
The critical detail is that jasmine.DEFAULT_TIMEOUT_INTERVAL relies on the real setTimeout to enforce spec timeouts. When jasmine.clock().install() replaces setTimeout globally, Jasmine's own timeout counter stops advancing unless you manually tick the clock. This means a spec that installs the clock and then hangs (e.g., waiting on a promise that never resolves) will not time out at the configured interval—it will hang indefinitely until the test runner's process-level timeout kills it.
Practical pattern: install the clock in beforeEach after any async setup that needs real timers, and uninstall in afterEach using a try/finally or the test framework's teardown guarantee. For suites that mix clock-dependent and clock-independent specs, consider a dedicated describe block with its own beforeEach/afterEach pair so the clock's lifetime is scoped to only the specs that need it.
Verification: set jasmine.DEFAULT_TIMEOUT_INTERVAL = 200, install the clock in a spec that never calls done(), and observe that the spec exceeds 200 ms without failing—confirming the internal timeout was suppressed.