Intermediate React, v2 Component Tests
This course has been updated! We now recommend you take the Intermediate React, v5 course.
Transcript from the "Component Tests" Lesson
>> Brian: Okay, so now inside of SearchParams.test, which we have up here, we're going to import React from react, import render and cleanup from react-testing-library. We're going to import pet, animals, and then breeds, and dogs from @frontendmasters/pet. And again, remember that it's going to know which one to grab this from.
[00:00:44] And then we're going to import searchParams from ../searchParams. So one thing that we need to do here for react-testing-library is we're gonna say afterEach, which is the function that Jest gives us, cleanup. And this is just a requirement of react-testing-library, you just have to do that.
>> Brian: Okay, and then we're gonna write a little test here.
[00:01:17] We're gonna test search params.
>> Brian: And this is going to be an async function so that we can do await. And we're gonna say const, curly braces, getByTestId, which is a function given to us, by calling render on SearchParams.
>> Brian: So this is going to go render it in Node, right, which is faster than just trying to render it to JSDOM or something like that or to have this Chrome.
[00:01:51] And then we're gonna say const animalDropdown = getByTestId('use-dropdown-animal') and then we're gonna say expect(animalDropdown.children.length).toEqual(animals.length). So what we're saying here is we're gonna go grab the animal dropdown inside of our SearchParams. And if I give it cat, bird, and dog, how many options do I expect it to have?
[00:02:44] Well, honestly, I expect it to have ANIMALS.length+1 right, because one of them is the all option as well. So if I add an extra animal in there, I would expect there to be an extra animal in the drop down. So if that's something that keeps falling out of sync or something like that, that's something we want to test, this is how you'd do that.
>> Brian: So there's something we have to go do really quick. I want you to go to use dropdown.
>> Brian: And here on this select, we're just gonna give it a data-testid. And then we're just gonna give it the same id as id right there. Now, why are we doing that?
[00:03:25] Well, it's good to decouple your testing logic from your normal logic, right? So, yes, this will get rendered out to the DOM and, no, it doesn't matter. But now we can actually individually address this by a test ID, which is in the end helpful, okay?
>> Brian: So now we need to go to our package.json.
[00:03:47] We have a test thing here. And finally this is going to do something instead of just yelling and exiting. So we're just gonna make this say jest. And Jest is quite smart, so it'll figure out how to run everything else. So now I can say npm run test.
>> Brian: And looks like we had an error. So let's go see what happened.
>> Brian: Unable to find data-testid use-dropdown-animal. So let's go see why that's the case. I thought we put data-testid id is equal to use-dropdown. And what did I put here? use-dropdown-animal. You know what I probably didn't do here?
[00:04:41] Yep, this is my problem, line 9. This has to be a component
>> Brian: So instead on line 9, you have that as a component. Now let's try that again.
>> Brian: Cool, now it passes.
>> Brian: So whenever I write a test that pass, the first thing I always do is like, I need to make sure this fails as well, right?
[00:05:12] So let's just go into useDropdown really quick. And right, you know, we have an empty option right there. Let's just add like three empty options, right? That should make it fail. Cuz then it's gonna have three more instead of just one more. Lo and behold, it says, hey, I got 11, and I expected 9.
[00:05:33] So I'm not happy about that.
>> Brian: So good, that's our good first test. Let's go write another one. So inside of SearchParams.test here, we're going to say expect(pet.breeds).toHaveBeenCalled, right? Because as soon as this thing starts up, it should call the API and say, hey, where are the breeds?
[00:06:05] And I'm going to say const breedDropdown.
>> Brian: = getByTestId('use-dropdown-breed'). And I'm gonna say, expect(breedDropdown.children.length)toEqual(_breeds.length + 1), right? So, if it gets back 5 from the API, it should have six options.
>> Brian: So let's give that a shot.
>> Brian: And it looks like we failed here. So pet.breeds wasn't called.
>> Brian: So, I wonder what this is. I think this looks like it's not getting the correct mock.
>> Brian: Cuz it's not getting Frontend Masters. Did I spell anything wrong? __mocks__ > frontendmasters > pet.js.
>> Speaker 2: Should that be @frontendmasters?
>> Brian: That's what it is. Okay, so let's try that.
>> Brian: Great, so now it's passing again.
>> Brian: Cool, so that means the API is definitely getting called, right? The, if we go back to the code.
>> Brian: The API is definitely getting called. And the API is then giving back the five breeds that you and I listed together, right?
[00:08:17] And then that is working correctly, right? That's assuring, right? Which is what you want out of your tests. So let's go one step further here.
>> Brian: And we have to get a couple more things out of this call right here. So we're gonna grab a container and a getTestById and we're gonna grab getByText.
[00:08:39] getByText, okay? So we want to see that at first it renders. Then we're going to simulate a click on the button, right. And they're going to see if it gets pets back from the from the API. So we're gonna say const searchResults = getByTestId('search-results'), and we'll have to go put this in here in just a second.
>> Brian: We're gonna say expect('searchResults.textContent').toEqual.
>> Brian: No Pets Found.
>> Brian: And then we're gonna say after that, fireEvent.
>> Brian: And we'll have to go get that, as well, right?
>> Brian: So we're gonna auto-import that from react-testing-library.
>> Brian: So we're gonna say getByText('Submit'), so we're gonna go find the Submit button, Submut, I can spell.
[00:10:09] Submit, and we're gonna give it a new MouseEvent of click.
>> Brian: Okay, so just so you know, I imported that fireEvent from react-testing-library right there in line 2.
>> Brian: And we're gonna say expect(pet.animal), animals, as it is, toHaveBeenCalled. So we're expecting it to go to the API. And then we're going to expect(searchResults.children.length).toEqual(_dogs.length).
[00:11:06] Now, we have a minor problem here. The minor problem is that the method to request pets won't work as an async await, which is how we implemented it in the first place. And there's not really a good way for us to work around that with other than monkey patching promises, which if that sounds scary to you, that's because it is, and we're not gonna do that.
[00:11:38] So for the sake of this particular text right now, I'm just going to go change how this is implemented in SearchParams. So here this is an async function, and it's not really buying us that much. We're just gonna say function pet, and we're just gonna say pet.animals. And then we're just gonna put this inside of .then animals.
>> Brian: And move setPets into here, and get rid of this.
>> Brian: So all I did was just re-implement this as raw promises instead of using async/await. And I had an at length conversation with someone that worked at Facebook about this like, can we not do this? And they basically said, they're trying not to, because Facebook has async/await everywhere.
[00:12:32] And they wanna do this as well. But for now, this is how you kinda use async await and test at the same time.
>> Brian: And we do have to go into results.js really quick.
>> Brian: And say here on the className of search, we have to give it a test ID, which data-testid='search-results'.
>> Brian: Okay, now hopefully, fingers crossed, that was a lot of code. npm run test, we should get a bunch more tests passing. And we did. So that means if we go back into search test.js, it's initially rendering no pets found, and then if I click, it'll call the API and then given a correct amount of pets, it's then going to call the API and get back a correct amount of dogs.
[00:13:35] And it'll render one pet for each one of those animals that it gets back from the API.