Hooks had been launched in React 16.eight in late 2018. They’re capabilities that hook right into a practical element and permit us to make use of state and element options like componentDidUpdate, componentDidMount, and extra. This was not potential earlier than.

Additionally, hooks permit us to reuse element and state logic throughout totally different parts. This was difficult to do earlier than. Subsequently, hooks have been a game-changer.

On this article, we are going to discover the way to take a look at React Hooks. We are going to decide a sufficiently complicated hook and work on testing it.

We count on that you’re an avid React developer already conversant in React Hooks. In case you need to brush up your information, you need to try our tutorial, and right here’s the hyperlink to the official documentation.

[Read: 7 debugging techniques for developers to speed up troubleshooting in production]

The hook we are going to use for testing

For this text, we are going to use a hook that I wrote in my earlier article, Stale-while-revalidate Data Fetching with React Hooks. The hook is known as useStaleRefresh. When you haven’t learn the article, don’t fear as I’ll recap that half right here.

That is the hook we can be testing:

As you may see, useStaleRefresh is a hook that helps fetch information from a URL whereas returning a cached model of the information, if it exists. It makes use of a easy in-memory retailer to carry the cache.

It additionally returns an isLoading worth that’s true if no information or cache is on the market but. The consumer can use it to point out a loading indicator. The isLoading worth is ready to false when cache or recent response is on the market.

A flowchart tracking the stale-while-refresh logic

At this level, I’ll recommend you spend a while studying the above hook to get an entire understanding of what it does.

On this article, we are going to see how we are able to take a look at this hook, first utilizing no take a look at libraries (solely React Take a look at Utilities and Jest) after which by utilizing react-hooks-testing-library.

The motivation behind utilizing no take a look at libraries, i.e., solely a take a look at runner Jest, is to exhibit how testing a hook works. With that information, it is possible for you to to debug any points which will come up when utilizing a library that gives testing abstraction.

Defining the take a look at instances

Earlier than we start testing this hook, let’s provide you with a plan of what we need to take a look at. Since we all know what the hook is meant to do, right here’s my eight-step plan for testing it:

  1. When the hook is mounted with URL url1, isLoading is true and information is defaultValue.
  2. After an asynchronous fetch request, the hook is up to date with information data1 and isLoading is false.
  3. When the URL is modified to url2, isLoading turns into true once more and information is defaultValue.
  4. After an asynchronous fetch request, the hook is up to date with new information data2.
  5. Then, we alter the URL again to url1. The info data1 is immediately acquired since it’s cached. isLoading is fake.
  6. After an asynchronous fetch request, when a recent response is acquired, the information is up to date to data3.
  7. Then, we alter the URL again to url2. The info data2 is immediately acquired since it’s cached. isLoading is fake.
  8. After an asynchronous fetch request, when a recent response is acquired, the information is up to date to data4.

The take a look at stream talked about above clearly defines the trajectory of how the hook will operate. Subsequently, if we are able to guarantee this take a look at works, we’re good.

Test flow

Testing hooks with no library

On this part, we are going to see the way to take a look at hooks with out utilizing any libraries. This can present us with an in-depth understanding of the way to take a look at React Hooks.

To start this take a look at, first, we wish to mock fetch. That is so we are able to have management over what the API returns. Right here is the mocked fetch.

This modified fetch assumes that the response kind is at all times JSON and it, by default, returns the parameter url because the information worth. It additionally provides a random delay of between 200ms and 500ms to the response.

If we need to change the response, we merely set the second argument suffix to a non-empty string worth.

At this level, you would possibly ask, why the delay? Why don’t we simply return the response immediately? It’s because we need to replicate the actual world as a lot as potential. We are able to’t take a look at the hook appropriately if we return it immediately. Positive, we are able to scale back the delay to 50-100ms for faster exams, however let’s not fear about that on this article.

With our fetch mock prepared, we are able to set it to the fetch operate. We use beforeAll and afterAll for doing so as a result of this operate is stateless so we don’t must reset it after a person take a look at.

Then, we have to mount the hook in a element. Why? As a result of hooks are simply capabilities on their very own. Solely when utilized in parts can they reply to useState, useEffect, and so forth.

So, we have to create a TestComponent that helps us mount our hook.

This can be a easy element that both renders the information or renders a “Loading” textual content immediate if information is loading (being fetched).

As soon as now we have the take a look at element, we have to mount it on the DOM. We use beforeEach and afterEach to mount and unmount our element for every take a look at as a result of we need to begin with a recent DOM earlier than every take a look at.

Discover that container must be a world variable since we need to have entry to it for take a look at assertions.

With that set, let’s do our first take a look at the place we render a URL url1, and since fetching the URL will take a while (see fetchMock), it ought to render “loading” textual content initially.

Run the take a look at utilizing yarn take a look at, and it really works as anticipated. Right here’s the complete code on GitHub.

Now, let’s take a look at when this loading textual content modifications to the fetched response information, url1.

How will we do this? When you take a look at fetchMock, you see we watch for 200-500 milliseconds. What if we put a sleep within the take a look at that waits for 500 milliseconds? It would cowl all potential wait occasions. Let’s attempt that.

The take a look at passes, however we see an error as properly (code).

It’s because the state replace in useStaleRefresh hook occurs outdoors act(). To verify DOM updates are processed well timed, React recommends you utilize act() round each time a re-render or UI replace would possibly occur. So, we have to wrap our sleep with act as that is the time the state replace occurs. After doing so, the error goes away.

Now, run it once more (code on GitHub). As anticipated, it passes with out errors.

Let’s take a look at the following scenario the place we first change the URL to url2, then test the loading display, then watch for fetch response, and at last test the url2 textual content. Since we now know the way to appropriately watch for async modifications, this must be simple.

Run this take a look at, and it passes as properly. Now, we are able to additionally take a look at the case the place response information modifications and the cache comes into play.

You’ll discover that now we have an extra argument suffix in our fetchMock operate. That is for altering the response information. So we replace our fetch mock to make use of the suffix.

Now, we are able to take a look at the case the place the URL is ready to url1 once more. It first hundreds url1 after which url1__. We are able to do the identical for url2, and there must be no surprises.

This whole take a look at offers us the arrogance that the hook does certainly work as anticipated (code). Hurray! Now, let’s take a fast take a look at optimization utilizing helper strategies.

Optimizing testing by utilizing helper strategies

Up to now, now we have seen the way to utterly take a look at our hook. The method shouldn’t be excellent nevertheless it works. And but, can we do higher?

Sure. Discover that we’re ready for a set 500ms for every fetch to be accomplished, however every request takes something from 200 to 500ms. So, we’re clearly losing time right here. We are able to deal with this higher by simply ready for the time every request takes.

How will we do this? A easy method is executing the assertion till it passes or a timeout is reached. Let’s create a waitFor operate that does that.

This operate merely runs a callback (cb) inside a attempt...catch block each 10ms, and if the timeout is reached, it throws an error. This permits us to run an assertion till it passes in a protected method (i.e., no infinite loops).

We are able to use it in our take a look at as follows: As a substitute of sleeping for 500ms after which asserting, we use our waitFor operate.

Do it in all such assertions, and we are able to see a substantial distinction in how briskly our take a look at runs (code).

Now, all that is nice, however perhaps we don’t need to take a look at the hook through UI. Possibly we need to take a look at a hook utilizing its return values. How will we do this?

It received’t be troublesome as a result of we have already got entry to our hook’s return values. They’re simply contained in the element. If we are able to take these variables out to the worldwide scope, it can work. So let’s do this.

Since we can be testing our hook through its return worth and never rendered DOM, we are able to take away the HTML render from our element and make it render null. We must also take away the destructuring in hook’s return to make it extra generic. Thus, now we have this up to date take a look at element.

Now the hook’s return worth is saved in end result, a world variable. We are able to question it for our assertions.

After we alter it in all places, we are able to see our exams are passing (code).

At this level, we get the gist of testing React Hooks. There are a couple of enhancements we are able to nonetheless make, equivalent to:

  1. Shifting end result variable to an area scope
  2. Eradicating the necessity to create a element for each hook we need to take a look at

We are able to do it by making a manufacturing unit operate that has a take a look at element inside it. It must also render the hook within the take a look at element and provides us entry to the end result variable. Let’s see how we are able to do this.

First, we transfer TestComponent and end result contained in the operate. We will even must cross Hook and the Hook arguments as operate’s arguments in order that they can be utilized in our take a look at element. Utilizing that, right here’s what now we have. We’re calling this operate renderHook.

The explanation now we have end result as an object that shops information in end result.present is as a result of we wish the return values to be up to date because the take a look at runs. The return worth of our hook is an array, so it could have been copied by worth if we returned it instantly. By storing it in an object, we return a reference to that object so the return values will be up to date by updating end result.present.

Now, how will we go about updating the hook? Since we’re already utilizing a closure, let’s enclose one other operate rerender that may do this.

The ultimate renderHook operate appears like this:

Now, we are able to use it in our take a look at. As a substitute of utilizing act and render, we do the next:

Then, we are able to assert utilizing end result.present and replace the hook utilizing rerender. Right here’s a easy instance:

As soon as you alter it everywhere, you will notice it really works with none issues (code).

Sensible! Now now we have a a lot cleaner abstraction to check hooks. We are able to nonetheless do higher – for instance, defaultValue must be handed each time to rerender despite the fact that it doesn’t change. We are able to repair that.

However let’s not beat across the bush an excessive amount of as we have already got a library that improves this expertise considerably.

Enter react-hooks-testing-library.

Testing utilizing React-hooks-testing-library

React-hooks-testing-library does the whole lot now we have talked about earlier than after which some. For instance, it handles container mounting and unmounting so that you don’t have to do this in your take a look at file. This permits us to concentrate on testing our hooks with out getting distracted.

It comes with a renderHook operate that returns rerender and end result. It additionally returns wait, which has similarities to waitFor, so that you don’t must implement it your self.

Right here is how we render a hook in React-hooks-testing-library. Discover the hook is handed within the type of a callback. This callback is run each time the take a look at element re-renders.

Then, we are able to take a look at if the primary render resulted in isLoading as true and return worth as defaultValue by doing this. Precisely much like what we applied above.

To check for async updates, we are able to use the wait methodology that renderHook returned. It comes wrapped with act() so we don’t must wrap act() round it.

Then, we are able to use rerender to replace it with new props. Discover we don’t must cross defaultValue right here.

Lastly, the remainder of the take a look at will proceed equally (code).

Wrapping up

My purpose was to point out you the way to take a look at React Hooks by taking an instance of an async hook. I hope this helps you confidently sort out the testing of any type of hook, as the identical method ought to apply to most of them.

I'd advocate you utilize React-hooks-testing-library because it’s full, and I haven’t run into vital issues with it up to now. In case you do encounter an issue, you now know the way to method it utilizing the intricacies of testing hooks described on this article.

The Toptal Engineering Blog is a hub for in-depth growth tutorials and new expertise bulletins created by skilled software program engineers within the Toptal community. You may learn the unique piece written by Avi Aryan here. Observe the Toptal Engineering Weblog on Twitter and LinkedIn.

Pssst, hey you!

Do you need to get the sassiest day by day tech publication daily, in your inbox, for FREE? After all you do: join Large Spam here.