Intermediate React, v5

Testing a Component

Brian Holt

Brian Holt

SQLite Cloud
Intermediate React, v5

Check out a free preview of the full Intermediate React, v5 course

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

Brian creates a __tests__ directory to hold all test files. Tests are written for the Pet component to ensure the component displays a default thumbnail when no image is provided and a non-default thumbnail when there is an image provided.


Transcript from the "Testing a Component" Lesson

>> So we're gonna go into source here, create a directory called __test__. What's the deal with the double underscore, aka, the dunder? I did not make that up, it's what it's called. It's a Python thing. It's a Python thing that means the name of this folder is special.

It's magical. It's magical because anything inside of there is assumed to be a test. In other words, if you create that folder, the test will assume any file that you put in there is a test and it will run it as part of your test suite. So I can call anything in here blah.js and vitest is gonna assume that blah.js is a test.

You don't have to do it this way. The other thing that you can do, outside of this directory, I can create a file called Pet.test.js or jsx. If I put .test or .spec in here, both of those will also be assumed to be a test. So either one of those is fine.

What do I do? I do both, cuz I don't know why, but I just always have. Pet.test.js, which is in the test directory. I can tell you why I do it. I like moving the test out of the main directory, so I don't have this super cluttered directory.

And then I like putting test in there because now if I search for files, I can just search for test and I'll see all of my test files. That's my reason. Either one of those is fine, one of those is fine, whatever you wanna do. You can also just go into your V test configuration and say, these are my tests, and that works as well.

Okay, So Pet.test.js, we're going to import expect and test from vitest. Import{ render} from @testing-library/react. And import Pet from ../Pet. I'm sorry, this has to be Pet.test.jsx not js. Okay, test put a useful message here of what it's actually testing, displays a default thumbnail, cool. And this takes in an async function.

I always make these async, because they can be and I can do await in here if I want to. I don't even know if I'm awaiting anything in here. I am, but I always make these async just by default. I'm gonna say const pet = render(< pet />) like that.

And then I'm gonna say const petThumbnail should be equal to = await pet.findByTestId)('thumbnail'), expect (petThumbnail.src) toContain ('none.jpg'). And then with vitest, one of these things that it does, as opposed to JS, which we did not have to do before, vitest is actually gonna take your test suite and run it in parallel so it can go even faster.

So make sure you're just at the end of calling unmount. So you need to clean up after yourself as well, so. This is not working yet, that's intentional just so you know. But this is kind of the methodology that I'm thinking here, I'm gonna go render something. I'm gonna go find the thumbnail, and I'll talk about this TestId in just a second.

And then I'm gonna make sure that the source contains none if I don't give it anything. You can imagine that I get a ticket report from users, like, hey, you have broken images on your homepage for pets that don't have images, what's up with that? I can go write this test, and then I can go fix it, and now I have a guarantee that I know I have fixed it, and I can just leave this to my testing suite to make sure that it doesn't regress again, right?

That's the idea here. Let's go test or talk about this findByTestId. So open your Pet.jsx, Not tsx, jsx. Pet.jsx here. Here on the thumbnail here, I want to give this a test ID, so I'm going to give it a, Was is it, data test ID? Yes, data-testid = "thumbnail".

Now, why am I looking for this rather than using a CSS selector or something like that? What's nice about using specifically the test ID, one, it expresses this is being tested, right, to a future person that's reading Pet.tsx or jsx. It's also saying that later, if I change how this UI works, I can just move what this is attached to, right?

Let's say it moved over here, I would just move it over there, and all sudden, it's testing this instead of testing that. It makes your test work independently of HTML and CSS structure. That's good, it's good to decouple those where possible. Doing data-testid allows you to do that.

Cool, we are still, We're getting closer, but we're not quite there. Let's try running npm test and see what happens. I think it's gonna, we're still gonna fail. Why is it gonna fail? So this already caught a bug for us. It cannot read properties of undefined length. If I don't give it any images, then the images.length there is gonna fail, because you're trying to call .length on undefined.

Cool, I already caught a bug, let's go fix it. So go back toPpet.jsx, and instead of saying images.length, we're gonna say images && images.length. Cool, it already caught a bug, fixed that, run it again, and now it's gonna say, hey, I'm trying to run without a router. I don't know how to do that, why?

Link, this, which comes from react-router-dom, depends on a router being above it. We're trying to render it without a router. So in this particular case, what you're gonna need to do is you need to come back to your test and you have to wrap this with a static router.

Okay, and then we're gonna have to import {staticRouter} from, and I think it's react-router-dom. Does that work or do I have to get it from react-router-dom/server? I think you do. Yeah, okay, react-router-dom/server. This is just a browser router that can work in node, right? Cuz we're not spinning up a browser, so we need it to work in a node environment.

Okay, now I think our test is passing. We wrote our first passing test, congratulations. We can now be fairly assured that our none.jpg will show up if we don't give it an image, awesome. So let's make sure that it works with a non-default. So I'm gonna say test(" displays a non-default thumbnail").

Just another note for whatever string you put in here, put the bare minimum amount of text necessary to know what test failed if this is read, right? Don't make it super verbose. Don't give me a novel about it. I don't want to read a novel, I just wanna know what test broke.

This is me shadow complaining about former coworkers. I had this one coworker, bless their heart, would write novels. I feel like I'd had to write a book report afterwards. All right, pet = render, Same thing, staticRouter. We actually probably could have just copied this. So let's do that.

Same thing here, except we're gonna give it images this time. So 1.jpg, 2.jpg, and 3.jpg. Okay, so now we have a pet, const petThumbnail = await pet.findByTestId, and we're looking for a thumbnail again. Expect, (PetThumbnail.src).toContain("1.jpg"). pet.unmount. Yeah, this needs to be async as well. Async, thank you. And there you go, two test passed

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now