Testing loading states

Learn how to write automated tests against your application's loading states.

Summary

By default, Acceptance Tests in Ember wait for all pending requests to finish before moving on to the next line. This makes testing loading states tricky.

Fortunately, the official ember-test-helpers packages includes two functions we can use to help us test our app's loading states: waitUntil and settled. Let's see how they work.

We'll start with this test:

test('visiting /post/1 shows the post', async function (assert) {
  await visit('/post/1');

  assert.equal(currentURL(), '/post/1');
  assert.ok(find('[data-test-post-body]'), 'post body should be present');
  assert.ok(find('[data-test-post-comments]'), 'comments block should be present');
});

This route shows a loading state for the comments area, but if we tried to assert against it the assertion would fail:

  test('visiting /post/1 shows the post', async function (assert) {
    await visit('/post/1');
+   assert.ok(find('[data-test-comments-loading]'), 'comment loader should be present');

    assert.equal(currentURL(), '/post/1');
    assert.ok(find('[data-test-post-body]'), 'post body should be present');
    assert.ok(find('[data-test-post-comments]'), 'comments block should be present');
  });

Again, this is because the comments request will have finished by the time the test reaches our assertion.

To get our app reliably into the state where the comments loader is showing, we need to first remove the await from our call to visit:

  test('visiting /post/1 shows the post', async function (assert) {
-   await visit('/post/1');
+   visit('/post/1');
    assert.ok(find('[data-test-comments-loading]'), 'comment loader should be present');

    assert.equal(currentURL(), '/post/1');
    assert.ok(find('[data-test-post-body]'), 'post body should be present');
    assert.ok(find('[data-test-post-comments]'), 'comments block should be present');
  });

This lets Ember move onto the next line without waiting for all the pending requests to finish.

Now if we run this test, it will fail, because we haven't given the app a chance to render at all. This is where the waitUntil helper comes in.

We can import it from ember-test-helpers (which is one of Ember CLI's default addons)

import { waitUntil } from 'ember-test-helpers';

and use it in our test to wait until the comment loader is visible:

  test('visiting /post/1 shows the post', async function (assert) {
    visit('/post/1');
+   await waitUntil(() => find('[data-test-comments-loading]'));
    assert.ok(find('[data-test-comments-loading]'), 'comment loader should be present');

    assert.equal(currentURL(), '/post/1');
    assert.ok(find('[data-test-post-body]'), 'post body should be present');
    assert.ok(find('[data-test-post-comments]'), 'comments block should be present');
  });

Note that we await it, since waitUntil returns a promise.

Now that our comment assertion is passing, we want to let the comments load before getting to our final assertions about the comments actually being rendered.

We can use the settled helper to tell Ember to wait on all outstanding requests.

Import it alongside waitUntil

- import { waitUntil } from 'ember-test-helpers';
+ import { waitUntil, settled } from 'ember-test-helpers';

and await it after our comment assertion.

test('visiting /post', async function (assert) {
  visit('/post/1');

  await waitUntil(() => find('[data-test-comments-loading]'));
  assert.ok(find('[data-test-comments-loading]'), 'comment loader should be present');

  await settled();

  assert.equal(currentURL(), '/post/1');
  assert.ok(find('[data-test-post-body]'), 'post body should be present');
  assert.ok(find('[data-test-post-comments]'), 'comments block should be present');
});

And now, we have a robust test, that's not dependent on any flaky timing values, and is able to consistently assert against the UI of our app's loading state!

👋 Hey there, Ember dev!

We hope you enjoyed this free video 🙂

If you like it and want to keep learning with us, we've written a free 6-lesson email course about the fundamental patterns of modern component design in Ember.

To get the first lesson now, enter your best email address below:

You can also check out more details about the course by clicking here.

Questions?

Send us a tweet:

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