Testing setTimeout/setInterval
Tags: javascript, asynchronous, jasmine, webdev
Published: 2019-08-21 14:34:40

---
title: Testing setTimeout/setInterval
published: true
description: Recently at a client, a question came up about unit testing functionality that used setTimeout and setInterval.
tags: JavaScript, Asynchronous, Jasmine, WebDev
series: Front-End Testing Patterns
cover_image: https://thepracticaldev.s3.amazonaws.com/i/f9yw73h875cmiae2j4hx.png
---

Recently at a client, a question came up about unit testing functionality that used **setTimeout** and **setInterval**.

The issue in this particular case was that there were several additional locations where setTimeout and setInterval had been implemented ... and the complete codebase needs to run before testing. Because all code runs, there are "bleed over" cases where ***other*** code is interfering with the tests.

## The Pattern

The pattern discussed would allow for both sets of functionality to be wrapped in such a way that they can be removed, as needed. This functionality allowed the IDs to be stored in a way that they could be removed as the tests iterated.

This pattern is ***a patch*** using a pattern I am somewhat uncomfortable with as a tester, but given the amount of code already in place, this seemed like a reasonable option.

```javascript
var timeoutIds = [];
var intervalIds = [];

var windowSetTimeout = window.setTimeout;
var windowSetInterval = window.setInterval;

window.setTimeout = function testSetTimeout() {
  var id = windowSetTimeout.apply(this, arguments)
  timeoutIds.push(id);
  return id;
};

window.setInterval = function testSetInterval() {
  var id = windowSetInterval.apply(this, arguments)
  intervalIds.push(id);
  return id;
};

afterEach(function() {
  timeoutIds.forEach(window.clearTimeout);
  intervalIds.forEach(window.clearInterval);
  timeoutIds = [];
  intervalIds = [];
});
```

## setTimeout

Now, having shown this pattern, I found a few other options that, while they seemed more reasonable, did not fit well with this established codebase.

The following examples were primarily derived from [How to test a function which has a setTimeout with jasmine?](https://stackoverflow.com/questions/10955201/how-to-test-a-function-which-has-a-settimeout-with-jasmine)

Part of the issue I see with these examples is that ***setInterval*** is not covered.

Given a function with a timeout inside ...

```javascript
var testableVariable = false;
function testableCode() {
  setTimeout(function() {
    testableVariable = true;
  }, 10);
}
```

Use **done** as a means to tell the test that the `expect` will be checked asynchronously, allowing enough time to expire for the setTimeout in the code above to run ...

```javascript
it('expects testableVariable to become true', function(done) {
  testableCode();

  setTimeout(function() {
    expect(testableVariable).toEqual(true);
    done();
  }, 20);
});
```

Additionally, the timer behavior could be mocked ... this method allows ***jasmine*** to step the time forward.

```javascript
it('expects testableVariable to become true', function() {
  jasmine.clock().install();

  testableCode();
  jasmine.clock().tick(10);

  expect(testableVariable).toEqual(true);
  jasmine.clock().uninstall();
});
```

And ... we can now use ***async/await*** ...

... from [Asynchronous Work](https://jasmine.github.io/tutorials/async)

This pattern actually means that `setTimeout` needs to be adjusted to allow it to `resolve` ...

```javascript
var testableVariable = false;

const sleep = (time) => {
 return new Promise(resolve => setTimeout(resolve, time));
};

async function testableCode() {
  await sleep(10);
  testableVariable = true;
}
```

Then, testing becomes ...

```javascript
it('expects testableVariable to become true', async function() {
  await testableCode();
  expect(testableVariable).toEqual(true);
});

```

Also, the original code could be refactored to take the function inside the ***setTimeout*** out in a way to make it testable.

```javascript
var testableVariable = false;
function testableAfterTimeout() {
  testableVariable = true;
}
function testableCode() {
  setTimeout(testableAfterTimeout, 10);
}
```

With this code, we can simply test the **testableAfterTimeout** function directly ...

```javascript
it('expects testableVariable to become true', function() {
  testableAfterTimeout();
  expect(testableVariable).toEqual(true);
});
```

## setInterval

Looking at another example ...

```javascript
var testableVariable2 = false;
function testableCode2(){
  var counter = 1;
  var interval = setInterval(function (){
    if (counter === 5){
      testableVariable2 = true;
      clearInterval(interval);
    }

    counter++;
  }, 500);

  return interval;
}
```

In this case, we should be able to see that the previous test patterns should work in our favor here.

Use **done** as a means to tell the test that the `expect` will be checked Asynchronously, allowing enough time to expire for the setTimeout in the code above to run ...

```javascript
it('expects testableVariable2 to become true', function(done) {
  testableCode2();

  setTimeout(function() {
    expect(testableVariable2).toEqual(true);
    done();
  }, 4000);
});
```

Additionally, the timer behavior could be mocked ... this method allows ***jasmine*** to step the time forward.

```javascript
it('expects testableVariable2 to become true', function() {
  jasmine.clock().install();

  testableCode2();
  jasmine.clock().tick(4000);

  expect(testableVariable2).toEqual(true);
  jasmine.clock().uninstall();
});
```

Also, the original code could be refactored to take the function inside the ***setInterval*** out in a way to make it testable.

```javascript
var testableVariable2 = false;
var counter = 1;
var interval;
function testableAfterInterval() {
  if (counter === 5){
    testableVariable2 = true;
    clearInterval(interval);
  }
  counter++;
}
function testableCode2() {
  counter = 1
  interval = setInterval(testableAfterInterval, 500);
  return interval;
}
```

With this code, we can simply test the **testableAfterInterval** function directly ...

```javascript
it('expects testableVariable2 to become true', function() {
  counter = 5;
  testableAfterInterval();
  expect(testableVariable2).toEqual(true);
});
```

## Conclusion

There are many means of handling Asynchronous behavior. I've only mentioned a few here (and in a specific test framework). These are simply a few patterns that can be used when these cases come up.