Skip to content

Impossible to restore fake timers in certain situations. #2449

@cjbarth

Description

@cjbarth

Describe the bug
When sinon.useFakeTimers is used in a nested fashion, or code crashes or early exist before a timer is restored, it becomes impossible to reset the timers to their default configuration.

To Reproduce
Steps to reproduce the behavior:

  1. Go to https://npm.runkit.com/sinon
  2. Run this code:
var sinon = require("sinon")
let fakeClock;

console.log("Original time: " + new Date().getTime());
fakeClock = sinon.useFakeTimers(Date.parse("2014-06-05T12:07:07.662Z"));
console.log("First fake: " + new Date().getTime());
fakeClock = sinon.useFakeTimers(Date.parse("2018-04-11T14:08:00Z"));
console.log("Second fake: " + new Date().getTime());
fakeClock.restore();
console.log("Expect either 'first fake' or 'original time'");
console.log("First restore: " + new Date().getTime());
fakeClock.restore();
console.log("Expect 'original time'");
console.log("Second restore: " + new Date().getTime());
fakeClock.restore();
console.log("Expect 'original time'");
console.log("Third restore: " + new Date().getTime());

Expected behavior
I expect there to be some way to get the original timers back.

Context:

When writing Mocha tests, often there are multiple nested describe blocks. If each block doesn't handle the timers correctly, particularly child blocks, then this can easily happen with no way to recover or reset. This gets particularly tricky if before* or after* blocks are used to fake timers and then an individual it block also messes with timers.

Additional context
Granted, all these cases above could be consider a bug or poor usage, but there really should be some way to get things back to normal. The problem was realized when trying to debug intermittent failures in tests (we randomize the order of our tests to catch stuff like this). It took quite a while to hunt this problem down. Then, the fix was rather painfully going through every place were a timer was used and making sure that, if a decedent test was using a timer, it was declared locally and cleaned up in a finally block. So, now our code has lots of try...finally in it where there was none before. Otherwise, an abnormal function termination would cause the fake timers to leak and break other tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions