Check out a free preview of the full Complete Intro to React, v9 course
The "Testing Custom Hooks" Lesson is part of the full, Complete Intro to React, v9 course featured in this preview video. Here's what you'd learn in this lesson:
Brian writes a unit test for the usePizzaOfTheDay custom hook. Since hooks can only be called from within a component, a test component is instantiated within the unit tests to mock the environment. The code is then refactored to use the renderHook method to render the hook without requiring a test component.
Transcript from the "Testing Custom Hooks" Lesson
[00:00:00]
>> Brian Holt: Let's talk about testing custom hooks. Like one of the big reasons that we actually want to use custom hooks, so we're gonna test, used piece the day here. But it's really nice that we have these custom hooks because it, they've become extremely easy to test. And then you can make sure, epecially if you have this important hook that's used in a bunch of places.
[00:00:23]
Again, if we're showing pizza everywhere, and we particularly use pizza today, we're gonna use this in 30 different places in our code bases. We can test this in various different ways to make sure that it works across various different environments, becomes a very well stable and reliable piece of code.
[00:00:41]
The thing that makes me laugh about some of these tests, though, is they almost always end up being longer than the component themselves, it's very normal. Create a new file here, call it use pizza of the day.test.jsx, okay? We're gonna say import expect, test ,vi, from the test.
[00:01:09]
Import render from at testing library slash react
>> Brian Holt: And import, create, fetch, mock.
>> Brian Holt: From the test fetch, mock, and then of course, import, so pizza, use pizza the day. Yeah, for whatever reason, I did this as a name export. It's so weird that I would do that, but I did, so we're going to import it that way.
[00:01:57]
>> Brian Holt: Use pizza up the day, like that.
>> Brian Holt: const fetchMocker equals createFetchMock, all things that we've done before, pass in vi, fetchMocker.enableMocks, there we go. We're gonna make a testpizza here.
>> Brian Holt: So I put calabrese in here.
>> Brian Holt: I think I literally just grabbed this out of the database.
[00:02:34]
So the calabrese pizza.
>> Brian Holt: Category, supreme, I guess it's a supreme style pizza and description. I'm not writing all that, so it's just gonna be something shorter than that. So lol pizza from Calabria and image is slash public, slash pizzas, slash calabriza dot webp. And then the sizes is, this is the prices, right, 12 dot 25, 16 dot 25, and the large is 20, 25.
[00:03:29]
Okay, good test pizza there.
>> Brian Holt: So, how do you render a hook outside of a component, that's a trick question, you can't right, a hook really works in the context of a component. So we're gonna say function, get pizza of the day and we're just gonna fake it out.
[00:04:00]
We're gonna say, let pizza
>> Brian Holt: Function, test component.
>> Brian Holt: We're gonna say, pizza equals, use pizza of the day and return null.
>> Brian Holt: We're gonna say render test component, and then we're just going to return pizza, right? That should work.
>> Brian Holt: So it's gonna create a test component that doesn't render anything, all it's gonna do is just call the component.
[00:04:42]
We're going to go as far to render the component and throw it away cuz we actually don't really care what it renders out at the end, it's gonna be null. But we're going to just keep that pizza variable that it gets back from use pizza the day, and we're going to return whatever that pizza is.
[00:04:59]
>> Brian Holt: What is pizza the first time?
>> Brian Holt: No, right, use pizza the day, what is it the first time, no, and then it cause it cause the use effect, right? And then that's what we're gonna get on subsequent, right? So, let's write a test of that, right? We wanna make sure that it's not giving us something weird the first time.
[00:05:21]
Gives null when first called, that's what we expect. Async function, and we're gonna say fetch dot mock response once, mock response once. I'm gonna say, JSON at stringify, test pizza, const pizza equals get pizza of the day, and expect pizza to be null.
>> Brian Holt: There we go, it works, let's go make it something else.
[00:06:26]
Use pizza of the day, we want this to now return.
>> Brian Holt: And there we go, it expected null and it got lol. So that's not gonna work, there we go, no.
>> Brian Holt: Now, you might be looking at this like, this seems like it's kinda weird, right? That we're like faking a component to get a hook out of it.
[00:06:56]
And I wanted you to see how to do this because I'm gonna show you how to just use the, what is it called, render hook function, which just does all of this for you. But I wanted to demystify a little bit as like what this is doing for it doesn't do very much, right?
[00:07:10]
It's just it's making a fake component. It's rendering it once and then it's giving stuff out of it. That's all that this thing does for you. But we're gonna make this so much simpler now. Get rid of all this just garbage, get pizza of the day, no one likes that, and we're gonna get this function here called render hook from React.
[00:07:32]
So instead of render, we want render hook like this. Then what we want instead is, on this line we're gonna say const, result equals, renderHook, and you give it a function to call, usePizzaOfTheDay like this.
>> Brian Holt: And then we're gonna expect result dot current to be null.
>> Brian Holt: This got a lot more succinct, right?
[00:08:12]
This looks a little strange, right, but at the same time, it's way less code that we have to write. All this is doing is this is creating a fake component underneath the hood and then calls this function, which is gonna call our hook for us. And then we're just pulling up the results there.
[00:08:31]
>> Brian Holt: Does that make sense?
>> Brian Holt: Yeah.
>> Speaker 2: Just wondering about the formatting on line 20 constitues results and canary brackets. Is that because these pulling it out, is it a more complex object and we're just taking something called results, okay, cool.
>> Brian Holt: Same thing, yep, you're just destructuring what you get back out of it, and let's make sure I'm not telling you lies.
[00:08:54]
But yeah, that that passes pretty cool, just love the v-test tools as well. Like they're just so nice to work with. It's very easy to run all of your tests all at once, or rerun certain ones of them, like let's say I only wanna run certain patterns of them.
[00:09:13]
You can hit f to only rerun failed tests, which I don't have any so it's not doing that right now, you can write a filter. So let's say I just want, I could put like contact and then it'd be like, okay, I'll only rerun that test for you.
[00:09:34]
It's just a very nice tool to work with, but it just hit h to show this and it'll show you all the different things that it can do.
>> Brian Holt: Okay, so let's add another test. It's not super interesting that it's just getting null the first time. So let's write another test here that we make sure that it calls the right API.
[00:10:01]
We're gonna add another one up here under testing library called wait for, we'll use that in just a second test to call the API and give back the pizza of the day, async. I can spell promise.
>> Brian Holt: Okay, fetch, you can actually just grab that from the top one here as well, so I'm gonna grab that, I don't even know if this is necessary, if we're being honest,
>> Brian Holt: Const, result equals render hook, same thing here.
[00:10:51]
>> Brian Holt: Use pizza of the day, I don't know why we're giving an empty string there, you don't have to, wait. Wait for, and it's kind of the same way with the one that we were using before. We were just saying, hey, wait till this happens and it looks like this.
[00:11:11]
So we're gonna say, wait for expect result fot current to equal testPizza, okay? And then you just expect fetchMocker to be called with, toBeCalledWith.
>> Brian Holt: Api slash pizza-of-the-day. So let me explain a little bit more on wait for. It's kind of an interesting API. Wait for, basically says run this function continually until it no longer throws an error.
[00:12:00]
What does expect do when it fails the test, throws an error, right? So you're basically saying like, run this expectation again and again until it doesn't throw an error, right? So until this test passed, essentially run this again. And this one will wait a little bit longer than our other one, I think It'll wait quite a while before, I don't say quite a while, but several seconds before it actually ends up failing.
[00:12:25]
So let's see how we did here. It did work, but let's just, if you did this, it should wait a while. Yeah, I mean it's like two seconds there at that time. But there you go, waitFor is quite nice as well. Now, this is a pretty well-tested little hook, right?
[00:12:48]
We can now be pretty confident in using this all over our code base. At least with null and with proper data, it's gonna work, right? So, I like that, you might need to test like an error case, like what happens if pizza of the day doesn't come back with something?
[00:13:02]
Or what if there isn't one for that day or something like that, you might have to add some of those edge cases. But at least the happy paths are all covered. The happy pizzas, as one might say.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops