This course has been updated! We now recommend you take the Intermediate React, v5 course.

Check out a free preview of the full Intermediate React, v4 course:
The "Basic React Testing" Lesson is part of the full, Intermediate React, v4 course featured in this preview video. Here's what you'd learn in this lesson:

Brian writes the first two tests for the Pet component. Using data attributes to give DOM elements test IDs is beneficial for decoupling the UI from the testing logic. The test:watch command is used to rerun tests automatically as the code is updated.

Get Unlimited Access Now

Transcript from the "Basic React Testing" Lesson

[00:00:00]
>> So I'm gonna make a new file here called pet.test.js. But Brian, why did you call it pet.test.js? You don't have to call it test.js. Force of habit. I also like the cool little special logo I get. It's kinda like a cute little vial there. I'm very easily entertained.

[00:00:21] But it's not necessary, I don't have to call it that. It's actually, to be totally honest, it's so I can find it quickly with Command+P or CTRL+P, right? Cuz now all I have to do is pet.test, and I know exactly which one I'm looking at. That's honestly the real reason why I do it that way.

[00:00:38] Okay, so the first thing we gotta do up here is, we have to give it a special comment to let us know this is going to be run in a browser like environment, jest.environment.jsdom. This is just telling just, hey, I'm gonna be doing browser leave things here, please instantiate js DOM.

[00:00:58] Js DOM is a fake DOM implementation. It is heavy because the DOM is heavy. So you only wanna instantiate it on tests that actually use it, which is gonna be most of your React test, so just get used to copying pasting this around. Okay, we're gonna import some helpers from jest import expect and test from @jests/globals, okay?

[00:01:26] We're gonna import render from @testing/array/react, that's goodness. And then we're going to import pet from ../pet Okay, and we're gonna write a test, displays a default thumbnail. I used to be really into the, it, should, that kind of terminology for testing. And it's like fluent testing or there's some term for it that no one cares about.

[00:02:12] And I came back around to is like, if something fails I just wanna know what what failed. So make sure whatever string you're putting here, if you see this test is failing, but you know what that test was supposed to do. So I think this is very clear.

[00:02:26] If it doesn't display a default thumbnail, then you know what's what's going wrong. Okay, I'm gonna make an a sync function here I'm gonna say const pet=render pet, like that. I'm gonna say const petthumbnail=await pet.findbytestid. Thumbnail. And expect pet thumbnail.source, to contain none.jpg. This doesn't work yet, by the way, just bear with me for a second.

[00:03:26] Okay, we're going to render our pet, right? Good, we like that. We're then going to look at what it renders to find the thumbnail, which we have not put this test Id in yet. I'll show you what that is here in just a second. And then we're gonna check to see, if I don't give it any images information, what is the thumbnail going to be?

[00:03:49] And we want it to be none, right? Which is our placeholder image. And you can imagine some situation where we were having foreign images or it wasn't displaying anything, you'd write this test to prevent that from happening, Okay? So now if we do NPM test. This is what I thought was gonna happen, okay, So we do have to install preset React.

[00:04:23] So one more thing that we have to do with our babelrc really quick, we have to do NPM installed capital P app babble/preset-React, okay? This one saw one more transformation that does all of our React transformations for us in our babel RC. I should have believed Pass Brian, Pass Brian was correct.

[00:04:47] We have to put this presets in here and we have to tell it that we want it to run. Preset, Let me just go back to my notes here, I can't remember this off the top my head. It is @babel/preset-React If you're following along with me, just totally go copy this out of my notes.

[00:05:21] There's no reason that you need to copy this by hand. You have to tell it the runtime is automatic Okay? And then down here you have to say, @babel/preset- nth, right, which is what we were doing down here. All right, and I think that should fix our problem.

[00:05:56] Cool, yes, that Is perfect. So to sum up here, make sure that you install preset-env, make sure you install preset-react. And then you need this babel RC to make sure that it's doing the correct transformations for production. So this first preset ones is going to do all the transformations for everything, whether it's in node or in the browser.

[00:06:21] Then this env one is specifically if node env=test, then transpile your code to target node js inside of the browser. That's everything that this babel RC does All right, now that we have chosen the correct go-to sacrifice, our tests are working. Well, they're not working, but they could work.

[00:06:46] [LAUGH] It's important. So our test actually caught a bug, right? So it's like, hey, I'm trying to run this with no images and images.length, this is not an array, therefore this doesn't work. So our tests have already caught a bug. That's a positive thing. So now head to your pet.js, and here's this images.length and the first thing that we have to ask here if images, right?

[00:07:24] If this is anything or you could do something like, array.js array, right? That's probably even better. Okay, so now that we put that in. Now we have a different error. So we solved that image or that problem. So this is actually a real bug that did exist in the code, which honestly I didn't intend to do.

[00:08:02] I wrote the entire course and came back later is like, this kind of bug that I intend on being there. But I left it in cuz that's how you're supposed to fix it. Now we have a different problem. This has a link in it. Link comes from React Router-DOM and React Router dash DOM does not work if it does not have a router that's encapsulating it.

[00:08:27] So, very easy fix to that. All we have to do is say, import static router from this will be familiar if you took the section on server side rendering. This will come from React Router Dom/slash server. And then here all we have to do. Cap this in a static router And that's it, I don't think we have to even tell it like a URL or anything like that, it doesn't matter now, right?

[00:09:06] So now we can do NPM test. Still not passing, but we're not getting that, use h=ref problem. Now we're trying to do this find by test ID, but we haven't actually set that yet, right? So this is what I want you to do with this, head back over to pet.js.

[00:09:27] And then here. We have this image here. This is our thumbnail. This is the one that we care about, right? So we're just going to say a data-testid=thumbnail. Okay, and then now if you'd run NPM test So, now what passes. So let's talk about why we're doing it this way.

[00:09:51] There's a couple ways you can do selectors here. I could totally query by CSS selectors like all right, give me the image tag that's inside of the image container or give me the, whatever, class name. Let me tell you why I think this is better. We're now decoupling the functionality and the testing logic, which I think is positive because let's say I move the thumbnail and it's down here now for some reason, right?

[00:10:16] As I said UI's thrash all the time. Because the test ID went with, it it doesn't matter that the CSS selector changed, right? And I hate finagling back and forth like, well what's the CSS selector now? I'm decoupling the display logic from the testing logic and I think that's a positive thing.

[00:10:35] Okay, that makes sense? And that's actually a big point inside of testing library that they try and emphasize, try and keep those two things as separate as possible. So, let me show you another very cool thing you can do. We're gonna do NPM run test:watch. Is it gonna make me do this with Git?

[00:11:12] That's kind of interesting. Okay, so you can just do a git init here. This isn't. No, it's not. All right, now it's a git init. Now I can do NPM run test watch. So I guess you have to be inside of a git repo for the tenth watch functionality to work.

[00:11:36] It's interesting, I didn't know that. Now I have this really cool front end to my testing suite and it's like, alright, you know what, I wanna run all my tests right now. I'll hit a, and they'll run all of my tests again. And I can go back and forth like, all right, now only run tests that have changed since the last time I updated this.

[00:11:52] So you can say, and this will run this because this is a new test, right? But if I have a bunch of tests that didn't run before, it won't run those. Or if I save this again, it'll automatically watch for tests and or files that are being imported by tests to change and then that'll only run the tests that I'm changing, right?

[00:12:10] So it gives me a really fast feedback cycle of things that are changing. Does that make sense? I love this, I actually use it all the time. Until we talked about the VS code extension, which actually makes this even easier. But for now, I'm gonna show you this, we'll stick with this for a bit.

[00:12:26] All right, let's write one more test for this. We're gonna say test, and we'll say displays a non default thumbnail. This is gonna look really similar, right? We're gonna go and say blah. We're gonna say, it's actually pretty much mostly this. We'll copy this, we'll paste this down here, but instead now we're gonna say pet and we're gonna say images=, and we're given an array of 1.jpg, 2.png and 3.webm, or something like that.

[00:13:09] So we would expect, and actually now it's failing, right, cuz I didn't change this. We'd expect the thumbnail to be the first thing in here, so it should be 1.jpg. Same thing here. This is good and then this is gonna be 1.jpg. And what did I not do here?

[00:13:33] This is an async function. Yeah, make sure that 21. It has to be async because we're doing a weight right here. And now it's passing, right? So if I give it images, it'll pick the correct one to display. And so, now I've pretty much written tests that assure me that No matter what I'm doing with all of my various different thumbnail logic, that both of those use cases are captured.

[00:14:05] But notice, this is from the perspective of the user as opposed to implementation details. I'm not looking for that Pet is storing the correct hook here. I'm not checking is hero set to the correct path or something like that? Or are the correct prompts being passed in? I'm checking, does the user see the correct image?

[00:14:27] Make it about the user don't make it about yourself. That's career advice for you right there. I'm actually kind of serious about that one.