
Intermediate React, v3 Writing React Component Tests
This course has been updated! We now recommend you take the Intermediate React, v5 course.
Transcript from the "Writing React Component Tests" Lesson
[00:00:00]
>> Let's write a test for pet.js. So we're gonna go into our directory here, where a new file and we're gonna call it pet.test.js. A lot of people will call these like spec as well. It's up to you. And I will even see people called like pet tests like that.
[00:00:20] I'm gonna say I like pet.test.js. Now it's very obvious when I'm looking through like my file switcher here. If I type test, all my tests will come up. Another note here, actually, if you have pet.test.js outside of the under double underscore test directory, it'll automatically run all of those files too.
[00:00:40] So actually could just call this pet.js and it would run that automatically. It doesn't have to be called that. But I like having both assurances. So I just, I do both. Okay. So first thing we're gonna do is we're going to import, expect and test from that at just/global's.
[00:01:13] Previously they were just global's, and you could use them. I like doing this explicitly so that one ESLint doesn't get upset and well, mostly that it's also explicit which is nice. Then we're gonna import render from our testing library/react. And we're gonna import pet from dot dot slash pet.
[00:01:39] Okay? So let's say we had a problem with like the thumbnails not working correctly. So let's try to test that would actually test that the thumbnail is showing correctly. So we're gonna write a test displays default thumbnail. So, here you wanna have a pretty descriptive string so that immediately if you read is like okay, I know which test is failing,that's the rule of thumbnails.
[00:02:15] Write enough text that you know which test is failing if that string pops up. Okay, then I'm gonna run Async function. I just make all of these async now even if there's not anything async about it just habit because eventually might be async and there's no reason not to.
[00:02:31] That's up to you. Okay, I'm gonna write cost pet equals render. And since this is going through Bible, we can just use jsx directly. Then I'm gonna say const petThumbnail = await pet findByTestId thumbnail. Now, what's a test ID? Well, we haven't used it yet, but I'm about to show you, and I use them pretty frequently.
[00:03:04] You can find things by roles, by alt text, but what it's gonna try and prevent you to do is using CSS selectors, cuz that's the temptation. What's really nice about this is if I use a test ID, then I can move that around if I reorganize my code and then my test might still work.
[00:03:22] Okay, and then already here I'm gonna write my test I'm going to expect pet thumbnail that source to contain none.JPG because I know that's the name of my default thumbnail, right? So this is a test I might write if my thumbnails just stopped showing up one time. And I could write this as soon as like, okay, now I'm positive if I didn't give this any sort of default image.
[00:03:54] So it'll now show a none.jpg. So this isn't gonna work quite yet. Well, it's because we haven't used this test idea. In fact, this won't even run if you try and do NPM test. So one fun thing that you can do with NPM. You can do NPM run test.
[00:04:18] But if you're really lazy, you can just do NPM T and we'll figure out what that means. Test. Formidable trick. So this is going to just air out in a really gross way. And why is that? Well, if you look inside a pet.js, it has a link in it.
[00:04:37] And a link cannot exist without a router. It just cannot do it. So we can kind of just cheat a little bit and say import static router from react router DOM. And we can just wrap this in a static router. In this case, it's really not going to do anything.
[00:04:59] It's just gonna make sure that it's in a correct environment where this pet can render correctly. I think it's static router, right? Yep, now, we can do an NPM test. It's not gonna find it, so that's gonna be an error, but now at least it's not erring in the same way.
[00:05:26] So there's actually even already caught a bug for us, which we can go fix. If we don't give pet an images array, it tries to find the length on it. And that's the problem right? It should. Now if I came in here and gave it an empty array here like this.
[00:05:48] Then that might work but we want to be able to give it nothing. So let's go fix that. Go into pet.js. And what we need to do to go fix that is we just say images and images.length. So that will actually fix that. So now we can actually run it without that.
[00:06:09] But let's go ahead and give the thumbnail, The test ID that we need to give it. So we're just going to give it a data-testid=thumbnail. Just looks like that. Now, when I say get by test ID, it's gonna grab this thing right here. And now, if I modify this later and I put like, I make the thumbnail, I don't know down here, all I have to do is move the test ID and it's now, it's gonna check that one instead.
[00:06:44] It makes it a little bit It makes your implementation logic and your testing logic separated from each other. Okay, now what happens if you do npm run test? Lo and behold, we have a passing test. Now what I wanna do here Is it's getting annoying to do run tests every single time.
[00:07:08] So I'm gonna do NPM run test colon watch. And now it's gonna put me an interactive test mode. This actually has to be a Git REPO or else it doesn't work. So you just have to say Git init This will put you into a Git repo now I can actually do run test watch.
[00:07:30] Here's my work if you already a Git repo mine just wasn't. It uses Git to check what has changed and what hasn't changed. That's how We have a passing test. We're running just an interactive mode. I can hit A, it'll rerun all of my tests over again, I can hit enter, it'll run just the tests again.
[00:07:53] I can run, I can hit O, it'll actually try and run only the files that have changed. There's a bunch of cool stuff that this can do. My favorite part of it though, is that I just like saved my test again. It'll run the tests that have set like you've saved recently.
[00:08:08] So now I don't have to do NPM run test every single time I can just be writing my tests and watching the bottom to see if things are working. So, let's write another test here. We're gonna test now if I give it a list of arrays, or a list of JPG so it will choose the right one for the thumbnail So let's do test displays a non default correct thumbnail Okay, write a function here.
[00:08:42] Same thing here. In fact, you can even copy and paste it. Okay? But now we're gonna give pet a set of images, images = I'm going to give it three images 1.jpg, 2. jpg, and 3. jpg. Now, we would expect that by default the thumbnail should be the first one, right?
[00:09:14] So I'm going to say I'm going to expect I can just copy this as well. I expect pet that thumbnail dot source equal to one jpg. And what did we do here?
>> Forgot async.
>> Yes, thank you. Make sure this is an async function. There you go.
[00:09:41] Now we have two passing tests for this. This is like the, for the most part, like how you test most react components. You're going to get them to render here using the testing library react. You're going to render all this stuff out. You're going to find whatever thing you need to test and you're gonna do a bunch of expects on that.
[00:10:03] That's the whole story here. Let's test some user, some interactions from the user, what happens if I click, or mouse up or something like that. So let's make a test for Carousel.test.js hearing tests. We're going to import, test or expect and test from @jest/globals. We're going to import render from At testing library/react, we're going to import carousel.
[00:10:49] From ../carousel. Gonna write a test that lets users click on thumbnails To make them the hero, right kind of that logic that we did that we click on a little thumbnail picture, and then it makes that like the focused big picture. I like this test cuz it's like it's something that a user would expect to see.
[00:11:16] And we're kind of testing it from the user perspective, like a user is going to expect to see this we want to assure ourselves that that continues to work, particularly if we've broken this in the past. So again, const images equals and we'll do zero dot JPG one dot jpg, two dot JPG and three dot jpg.
[00:11:42] Do, okay const carousel carousel is one of those words I just have a hard time spelling. Alright, carousel equals render. This one doesn't have any links in it so we don't have to wrap it in static router. But we could if we had to, carousel Images = images.
[00:12:11] Okay, so the first thing we do, we're gonna test the correct one is the one that we have selected already. So const hero equqls a weight Carousel.find by test ID. So let's go put that in there as well find by test ID, hero, and we're going to expect, That hero.source to contain Images zero.
[00:12:47] Right? Makes sense by default, the hero is going to be the first one. Assure ourselves that that's the case. Then what I want to do is I want to do a for loop and click on each one of them and make sure that it changes correctly every single time.
[00:13:00] We're gonna get kind of two benefits out of this. If I click on the first one. I mean, that's already the hero image, it shouldn't change, right? And then every one of them we're gonna make sure that it advances correctly. So I'm just gonna read a quick four loop here, four that i equals zero.
[00:13:15] i is less than images.length i++. And now const image equals images of I. Then I'm gonna say const thumb equals, A weight carousel.FindByTestId. We're looking for a thumbnail, Of i right? So, we wanna find like thumbnail 1 or 2 or 3 Okay. Then I'm going to do a thumb dot click.
[00:14:11] This is gonna be like a user actually clicked on that particular element. And then I'm gonna expect two things I'm gonna expect. Hero dot source toContain(image) and then I'm going to expect(thumb.classList).toContain("active") because when I click on that But particularly the thumbnail, it's gonna have an active class of like this is the active thumbnail at the moment, right?
[00:14:50] There you go. This won't work yet, we still have to go into carousel because there's none of those test IDs exist yet. So this is going to fail up first. So, inside a carousel, this thumbnail here. Going to give it a data. Test ID equal to, thumbnail. And we have to give some sort of I too it.
[00:15:23] So that's what we'll do up here. All this we already get index, so index. Okay, so our thumbnail's testid is taken care of. We just have to give it here to this one. So we'll do data.testid=hero. There you go. We are passing tests now. Now something I always like to do when I'm writing this test, I like to make sure that it fails when I would expect it to fail, right?
[00:15:55] So let's just say I accidentally made the source here. I'll just make this 5.jpg. Now if I save this. Well, that's not failing correctly, is it? It's cuz it's not checking the source on the thumbnail. It's checking it on the hero. Makes sense. So I'll just put zero here instead of active.
[00:16:27] And that's going to fail, right? Because the hero image is not changing, right? I just always put it as the 01. Then we change it back to active. Everything works. Cool. So one, I love this interactive test watcher. I love it that I can just rapidly iterate on something.
[00:16:52] Make sure that's working, make sure that's breaking. All that works really well for me.