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.
