๐ฅฝTesting Asynchronous Code
Problems faced in testing asynchronous code, and how to solve them.
Problem
Consider we have the following component in our app that renders a list of posts:
Now in our test suite for Async component we want to test whether or not we have all the test list items. We use the following test code:
NOTE: The role for list items are listitem
, as defined here.
By looking at the test code, it looks as our test should pass without any problems, but it is not the case!
Test Failure
When we run the above test suite, we end up with the test failing, with the following result:
We see that the error states the listitem
element is not accessible. This is because when we render the Async
component, the initial state is an empty array, on the initial render, the useEffect
hook does not run, which results in posts
state to be an empty array. After the component is mounted the useEffect
hook runs, and then the data is fetched from the URL
, which takes a few milliseconds, and then finally the posts
state is updated.
But when we run our tests, we are not waiting for the request "Promise"
to be completed, due to which we see that the listitem
element is an empty array with length 0
. We immediately get all the items on the screen with getAllByRole()
function.
Solution
We can overcome this problem of the getAllByRole()
function fetching the values immediately by using a "find"
selector from the screen property. The difference here is that all the "find"
selector functions return a Promise
, which needs to be fulfilled, before the element can be selected from the screen
. This is not the case with the "get"
and "query"
selectors, where they return errors
or null
/ not null
respectively.
Using findAllByRole()
findAllByRole()
For the Async
component test suite, we will use the findAllByRole()
method to find all the elements with the role of listitem
. This will now wait for the HTTPS fetch()
request to finish. It has a default timer of 1 second, that will return an error if the fetch()
doesn't return the data on time. We can change this default timer in the third parameter of the findAllByRole()
method.
Since this method returns a Promise
, we need to await
it so that it can complete. For this to work, our test()
anonymous function, i.e the second argument in the test()
function, can have the async
keyword, to wait for the Promise from the findAllByRole()
to complete.
Results
With the above changes, our Async Component Test Suite
should also pass without any errors.
Next Steps
This solution here is okay, but still not ideal. For a better approach and solution to testing async code, refer to Mocks.
Last updated