Testing UIs is tricky. One reason for this is the async nature of JavaScript, and of the SPA ecosystem in particular. This is what we’ll dive into today. We’ll look at how async code makes it difficult to know when something happened and then learn how we can use await until(...) to overcome it.

This is a complement to Testing React Components, a short guide for testing UI components with ease.

Can I assert now? What about now?

Picture this scenario: On mount, our component makes a server request and uses the response to generate a new state, which in turn activates a specific render path. More concretely, the component fetches a list of users from an endpoint and displays it. This is precisely how our test should read.

For simplicity, this is a pseudo test:

/users is mocked to return ["Mary", "Jason"]

is mocked to return Component is mounted

Expect: Component to have rendered Mary and Jason

Right. So why isn’t this article over?

Because the API call — even when mocked — is asynchronous. This means that Mary & Jason are scheduled to be shown as soon as possible, but our expect call runs first (and fails). Our eyes perceive the rendering as instant, but it’s too little too late for our heartless test. So how do we make our test wait?

Here’s a StackOverflow-esque response:

// Step 1: Mock API

// Step 2: Mount component setImmediate(() => {

// Step 3: Assert

});

And thus we surrender to the mercy of the JS event loop…

Note: If we could reference the Promise (or other async construct) returned by our actions, we could chain our assertions to be executed at the right time. But we rarely can. Components are free to dispatch async actions at their discretion, completely opaque to their public API.

But scheduling an assertion for the immediate future doesn’t come close to a reliable solution. Sometimes we have a chain of async actions that need to resolve before our state is ready for assertion. Eg. Our component reads something from an async local storage and then requests server data.

No problemo. This is me at 3AM,

// Note to future devs: Duplicate this as needed

await new Promise(resolve => setTimeout(resolve, 0))

await new Promise(resolve => setTimeout(resolve, 0))

But we don’t want to be in the spotlight when someone git blames that code. And as I mentioned before, testing should make us confident. So I propose we borrow a common end-to-end testing practice and introduce the await until primitive to component testing.

await until(…)

Let’s make our previous example concrete using async/await, Enzyme and the Cosmos Test API.

The Users component in this example renders a .loading element while data is being fetched, which is our cue for knowing when to assert. But the until callback can be set to “listen” to any condition, like whether or not the .users element exists.

await until(() => getWrapper('.users').length > 0);

If the condition isn’t fulfilled in a few hundred milliseconds (configurable) it throws an error (also configurable), which means that await until can even be used as a standalone assertion.

Note: This technique isn’t restricted to React components testing. It’s just a small JS utility that returns a promise that resolves when a given callback returns true.

Can I use it?

Natürlich! I published async-until on npm and you can find the source at github.com/skidding/async-until. Beware, the README is tiny.

I hope this will prove a useful tool in your arsenal for testing UIs with ease.