Instead of setTimeout, use run.later

Understand why setTimeout poses a problem for testing, and learn how to fix it

Summary

run.later is a function provided by Ember that is equivalent in functionality to JavaScript's native setTimeout.

We start off with a working Ember application that has an action using setTimeout:

deletePost() {
  let post = this.get('post');

  return post.destroyRecord().then(() => {
    this.get('after-delete')();

    setTimeout(() => {
      this.get('flashMessages').success('Post successfully deleted!');
    }, 1000);
  });
}

This works in development, but poses a problem for us when testing. When we try to write an assertion against the flash message we display in the callback of setTimeout, we get a test failure.

test('I can delete a blog post', async function(assert) {
  server.createList('post', 3);

  await visit('/posts');

  assert.dom(testId('post-row')).exists({ count: 3 });

  await click(testId('delete'));
  await click(testId('delete-post'));

  assert.dom(testId('post-row')).exists({ count: 2 });
  assert.dom(testId('flash-message')).hasText('Post successfully deleted!');
});

This happens because Ember's testing setup is configured to wait for certain asynchronous behavior, but it cannot detect all async code.

Some of the async code that Ember comes pre-wired to detect are

  • certain AJAX requests
  • pending items on Ember's run loop
  • test helpers that are configured to tell Ember testing when to advance

In our case, setTimeout isn't covered by the default testing setup.

To fix this, we can use Ember.run.later instead of setTimeout. It performs the same functionality, but makes itself aware to our test suite.

+ import { later } from '@ember/runloop';

- setTimeout(() => {
+ later(() => {
    this.get('flashMessages').success('Post successfully deleted!');
  }, 1000);

later is an exact test-aware replacement for the functionality we had in our app, so in this case we can just drop it in, and our tests pass. In the future, we'll need to do more work to make our application code testable.

Questions?

Send us a tweet:

Or ask us in Inside EmberMap, our private Slack workspace for subscribers.