Testing asynchronous components

I had a couple of gaps in my tests (I wasn’t testing the ConnectedPost) and I ran into the issue again where the HTML was not updated after an asynchronous call to the server. I did a a little research to figure out why.

When you make a call with await in a function, like this:

  async componentDidMount() {
    const post = await fetch(this.props.id)
    this.setState({post})
  }

Javascript returns a promise which gets resolved later. The React framework takes care of that eventually but, when you render your component in a test, you need to make sure all the promises are resolved before you check the resulting HTML.

Jest gives us a function to make that happen and I am going to add a helper to my React helper file to make it easy to use.

// ReactHelper.jsx
export const resolveAllPromises = () => new Promise(setImmediate)

Whenever I test a component that makes asynchronous calls, I have to remember to call resolveAllPromises() and make the test function async, like this:

// posts/Post.test.jsx
  test('fetches a post from the server', async () => {
    server.send.mockReturnValue(post)

    const component = display(<ConnectedPost id={1}/>)
    await resolveAllPromises()
    component.update()

    assert_select(component, '.post .title', 'The title')
    expect(server.send).toBeCalledWith('/posts/1.json')
  })

But wait! Why should I have to remember do call that every time? I can add it to the helper like this:

// ReactHelper.jsx

export async function displayConnected(component) {
  const connected = mount(component)

  await resolveAllPromises()
  connected.update()

  return connected
}

And now the test becomes much simpler:

  test('fetches a post from the server', async () => {
    server.send.mockReturnValue(post)

    const component = await displayConnected(<ConnectedPost id={1}/>)

    assert_select(component, '.post .title', 'The title')
    expect(server.send).toBeCalledWith('/posts/1.json')
  })

I’m glad that’s straightened out. Back to the storyline.