Welcome to FullStack. We use cookies to enable better features on our website. Cookies help us tailor content to your interests and locations and provide other benefits on the site. For more information, please see our Cookies Policy and Privacy Policy.
React Hooks Async Requests testing with TypeScript and Jest
Written by
Israel Rosales
Last updated on:
October 1, 2025
Written by
Last updated on:
October 1, 2025
Most tutorials on testing in React cover easy cases, but in real life the most difficult test cases are asynchronous request calls to APIs, how components change state, and how those changes affect the view. Let me share my method for testing async API calls using Jest and TypeScript in a CRA app.
Why this is needed
While learning to program, most of the time is focused on getting functionality to just work. When the code is related to web development, most people prefer to just open a browser and inspect the result visually. But a company that builds software needs to be sure the code doesn’t only work but is also reliable. To this end, writing tests for your code has proved to be the best way to make sure your codebase is reliable.
In the last edition of StateOfJS, a developers survey focused on the JavaScript environment, the section for testing shows that the tools available in CRA, Testing Library and Jest, are the highest-ranked by all the community, which shows how important is to know these tools and how much a good understanding is needed of using those libraries for testing.
A recap on React Testing
Newly created Create React App has support for testing. You can jump directly to your terminal and type npm test or yarn test and you will see a basic test passing.
With the tools already installed you can do a lot of testing. For example, if we create a basic component you can test its props, presence, or even HTML element ids.
Also, you can use snapshots, which ensure the component always renders as expected or as intended by the programmer.
But in a professional environment, we can face more difficult cases and maybe you are here to learn about one particular kind: async requests in React. Let me present three common problems while testing in CRA, and then how to solve them.
Some problems with default libraries for testing in CRA
Let’s change our last code to reflect some new functionalities. For example, let's add a state, as well as some hooks to the mix in order to show a more “real world” usage. Then let's identify some problems we can find while testing this new code.
Not detecting changes in state
Our new code will have some states that would be changed on mounting:
If I try to test a change in state, for example, if onLoading changed to false, I expect the element to not load but to show the result. But if I try just to search for the result element, we would see the change is not detected by Jest:
As you can notice by running the app, the state changes, it works, but Jest is not able to “see” the change.
Not detecting changes in props from Providers
Now we will consider other common problems in testing. When you have a provider way high on the tree and it changes props, this is hard to test. For example, let’s add AuthProvider, and let’s have certain component changes, for example, a certain param.
This is not a problem in the library react-router, as we can check using a different provider — context provider in this case. Let’s save a state in a context and add a setter, then the component receives this state from context and changes the text.
The problem is about Jest not being able to detect changes in props coming from a parent’s provider.
Not detecting async promises resolve
Most frontends for companies are meant to work with some API, so testing requests to an API is basic. But trying this in CRA has the same result as the other two problems exposed. Let's add fetch (fakeCall) when the component is mounted.
Again, we notice Jest fails. As these are fairly common cases we have to check how to test them. Let us analyze first why this problem happens and then we can check an easy way to solve each of the three cases mentioned.
Why testing async functionality is hard for default CRA libraries
Jest typically expects to execute the tests’ functions synchronously. If we do an asynchronous operation, but we don't let Jest know that it should wait for the test to end, it will give a false positive.
The problems exposed before are just cases of async functionality that face this problem in jest.
How to test async functionality
Testing changes in state
In order to wait for all state changes to consider the component completely rendered, we need to wrap the render with the act() function, which will make the component wait for all asynchronous processes in the component render to be completed, including all state changes that make the component render again.
The result of the component render should be assigned to a variable outside of the scope of the act callback, named component in the case below. This way, once all renders are completed we will have the last version of the component, and then we will be able to check the document to expect certain things to be found on it, as shown below.
If we don’t wait for the component to be completely rendered, we will just get the first mounted version, and maybe not get the expected results. In the example below, we’re looking for an element with the test id attribute Div::Result. We should see this element in the document only when the isLoading state changes to false.
Testing change in props from providers
When the component that we want to test consumes data from a context in the upper level of the app tree, what we need is to mock the return value from that context, as we need to see current component behavior, based on certain values of the context. This can be done by mocking the context module using jest.mock() function.
In the case below, useAuthContext provides us access to the context and all values inside, that’s the function we need to mock to determine later what we need to get returned. Also, note that we’re using jest.requireActual() to tell the mock that everything else that can be used from this module mock — other functions or objects — will stay as the real module.
Before rendering the component inside the act wrapper (explained on the last point), we need to tell the test what we need to get returned when useAuthContext is called in the component. After this, the test can continue by wrapping the render in the act() function and checking the presence of certain things in the document.
Another important thing to note is that if we mock the context module, we don’t need to include it in the component’s render, as the test will understand that AuthProvider has also been changed by a fake provider.
Testing async promises
Usually, we don’t need async API calls to be executed when testing a component, as the act wrapper doesn’t wait for it to be completed. The solution is to mock the service layer module. In the same way as we mock the context module (point 2), we can mock all of the API call functions that are being used in the component. Then, depending on what behavior we need to test, before rendering the component we need to tell the test what we need to get from the resolved (if we want to simulate a successful API call) or rejected (if we want to simulate an error while performing the API call) promise.
The object that we’re mocking should match with the one received from a real call. That way, we can expect normal behavior in the component test. After this, the test can continue by wrapping the render in the act() function and checking the presence of certain things in the document.
Conclusion
The team and contributors behind Create React App have done great work helping us developers to test common and some uncommon behaviors on React apps. As we noted, some common problems found while testing asynchronous code can be tested and we can get great coverage in our unit tests.
Testing asynchronous components is challenging because updates often happen after background operations, like API calls or context changes. If the test checks the screen too early, it may miss these updates, resulting in false positives.
How can I make sure my test sees updates?
You need to let the component finish all of its updates before checking the results. That means the test should wait until everything is fully rendered after any background operations.
How can I test a component that depends on context or parent props?
Instead of using the real parent provider, you can simulate or “mock” the data the component receives. This allows you to control the values and verify the component behaves correctly in different scenarios.
How should I handle async API calls in tests?
Instead of calling the real API, mock the functions your component uses. Return resolved or rejected promises depending on the scenario you want to test. Then verify that the component updates correctly based on the mocked response.
What’s the key to reliable async tests?
Always wait for all updates to finish, mock external data sources, and simulate API responses. This ensures tests reflect what the user actually sees and provides accurate, dependable coverage.
AI is changing software development.
The Engineer's AI-Enabled Development Handbook is your guide to incorporating AI into development processes for smoother, faster, and smarter development.
Enjoyed the article? Get new content delivered to your inbox.
Subscribe below and stay updated with the latest developer guides and industry insights.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.