Check out a free preview of the full Complete Intro to React, v9 course
The "Write a Unit Test with Vitest" 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 introduces testing and installs Vitest, a testing library for Vite. A basic unit test is written to ensure images have ALT text.
Transcript from the "Write a Unit Test with Vitest" Lesson
[00:00:00]
>> Brian Holt: We're doing everyone's favorite thing now, which is testing. [LAUGH] Some people get pumped about it, other people are like, why are you wasting my time? I'm probably somewhere in the middle. I like tests when they prevent me from wasting time in the future, and I hate tests that waste my time now and don't help me in the future.
[00:00:24]
So, again, I've been all over the years of how I felt about testing. I went all the way from, I can't be bothered, I got to ship stuff. I've been all the ways like, let's do TDD, I've never actually been that militant about it. And I'm kind of more back towards, let's write less tests that I like, and let's not write tests that don't tell me anything.
[00:00:46]
So I'm on the side of the camp that's, let's test less, yeah.
>> Speaker 2: Steve Kenny just released a testing fundamentals course. And I liked one thing he said, which was, when you get to a point where you feel overwhelmed with how many things are going on in your head as far as the business rules and the code and whatever.
[00:01:04]
When you feel that sorta overwhelm or anxiety, at that point, that's a really great place to introduce tests cuz it can kind of reduce that cognitive load that you're gonna break something or forgot to think of a certain use case, or whatever.
>> Brian Holt: That's a good way of putting it, I like that.
[00:01:23]
Steve's a smart guy when it comes to that kinda stuff, too, so I would say definitely check out his course. I wanted to show you how to test this app, cuz I think there's some good things to do in here, but I'm not the person that writes a ton of tests.
[00:01:44]
>> Brian Holt: The place where I find tests super useful is, when someone gives me an issue that here's a bug that we need to go fix, I actually find a really good way to start is to write the test that would've caught that. And then you can be kinda confident later when you come back to it later, it's like, okay, I wrote this test beforehand, now it's passing.
[00:02:02]
Now I can launch it, and now I have a test to go out to production with it that probably won't regress you this way again.
>> Brian Holt: [COUGH] But I'm a really big on, I want tests to fail fast, I want them to fail loudly, and I want them to be testing something important.
[00:02:20]
And if it's not doing all three of those things, I generally don't want that test, right? I like integration tests, big suites that cover user experiences, around something like, this user can log in and buy something, right? But I only want a few of those, cuz if you've ever maintained big suites of Selenium tests, you know what I'm talking about where you spend half of your life fighting tests, where it's false positives or false negatives or something like that.
[00:02:49]
So in those cases, I want a few tests that I care deeply about, and then if I don't care deeply about it or it's too finicky, I just delete it. Deleting tests should be something that you do on a regular basis, I do feel strongly about that. So, now that I've given you all the disclaimers of your learning from a crotchety old man about testing, I just wanted you to know my perspective on it.
[00:03:16]
There's other people out there that are way more, let's test all of the things that I'm more towards, let's test fewer things kind of side of the camp. So we're gonna test with Vitest today, which is a really cool thing made by the Vite people themselves. It's really nice because we don't have to set it up again.
[00:03:35]
In the old days when we were writing Mocha tests and stuff like that, you had to set up everything by hand. And the nice thing about Vitest is that it reads your Vite config and it just uses that, which is nice cuz we don't have to write any config, I don't think, not for a while at least.
[00:03:48]
It's designed to be a drop-in replacement for Jest, which is what I used to teach for this class. Jest was made by Facebook and is now OpenJS, so it's a totally open foundation. And Vitest is just mimicking what Jest is like, and Jest was mimicking what Jasmine was like, right?
[00:04:05]
So if you've written Jasmine, Jest or Vitest, they're all mimicking Jasmine originally. I think I even taught Jasmine for one of the courses here. Certainly Mocha, I've taught a lot of different testing software because the flavor of the day tends to change quite a bit. Let's talk about Vitest.
[00:04:23]
I'm gonna cancel my Vite server here for a little bit, I'm just gonna copy this from my notes here, Vitest, and then testing-library and happy-dom. So just to show you here, vitest 2.1.3, testing-library/react@16.0.1, and happy-dom at 15.7.4. Okay, Vitest, I just explained to you what that is, that's the testing suite harness for Vite.
[00:05:01]
Testing-library/react, this is a bunch of helper tools for writing react tests. It is definitely the one that everyone uses now. Years and years and years ago, Airbnb released one called Enzyme that people used for a long time, and it was finicky as hell. And now people use this instead, this is much easier to use, so definitely use this.
[00:05:22]
And then happy-dom is a synthetic browser environment. So we used to use one called jsdom. It was just massive and it was quite slow, and someone got kinda sick of waiting on tests apparently. And so they stripped that down and made happy-dom, which is the much smaller version of this that goes much faster.
[00:05:41]
Has the benefit of being faster, has the downside of being less complete, right? So if you're doing strange stuff with browsers, which we all want to do, happy-dom might fail or might pass unexpectedly. So do be aware of that. That being said, I've never had that problem, to be honest with you.
[00:06:04]
>> Brian Holt: Cool, all right, so we have all those things now. We're gonna go inside our directory here, and we're gonna make a directory called,
>> Brian Holt: __tests__. So again, this is a Python thing that they kinda brought over to JavaScript, the double underscore on either side. And that is a signal to any future user that it's a magical name.
[00:06:34]
I'm not making that up, that's what they call it. And it's magical in the sense it's important that it be called that, because the library that it's going to be used with is looking for that name specifically. So the double underscoring on the side means don't change the name of this or you're going to break stuff, right?
[00:06:52]
>> Brian Holt: That's why you see the underscore, underscore, in it, __.py for Python stuff. That's why it's called with the double underscores.
>> Brian Holt: Okay, so whatever we put in here, Vitest is just going to assume as a test, so you can call it whatever you want. I'm still gonna call my stuff app.test.jsx, or app.spec.jsx, either one is fine, but you don't have to if it's under the double underscore directory.
[00:07:23]
By the same token, I'm not gonna do this right now, but you can also just put it directly here. And it's also looking for files called app.test, something.test.jsx. Both of those, Vitest will find your tests automatically and run them. For some reason, I'm gonna do both the double underscores and the dot test.
[00:07:48]
>> Brian Holt: Okay, so go to package.json. We have all of our Vitest stuff in here, right there. Test, just change this to be vitest, like that, that's it, and it's smart enough to be able to read the vite.confit.js. So we don't have to configure it any more than that.
[00:08:11]
There's one thing we need to configure, but we can put it directly into our vite config, we don't have to make a new file.
>> Brian Holt: Okay, so in your vite directory here, we're just gonna add one more thing, test: { environment:, and we're gonna do it in "happy-dom".
[00:08:33]
Just have to tell which browser environment that you want to do it in. Momentarily, I'm gonna show you what's coming in Vitest, which they're going to do it with Playwright. That's kind of the new hotness coming in, and I tried to build this course with Playwright, it's just not ready yet.
[00:08:54]
I just didn't feel comfortable telling you to do it yet, so we're gonna stick with happy-dom. I'm going to show you how to set up Playwright towards the end, but just know, there's still gremlins and goblins in there, you gotta be careful. Okay, so we're ready to go, everything's ready to write tests.
[00:09:11]
Just so you know, if you do need to write a specific vite config for your vitest environment, you can write vitest.config.js. And this will be a specific vite.config for your vitest environment. We'll do that actually towards the end. Just be aware that you have to tell it to extend your current vite.config, or else it will ignore it, and then you can get into weird places.
[00:09:37]
But again, we'll talk about that here in a bit. We don't have to do that now because luckily everything remains the same for us. So we're gonna create a file called Pizza, and you can either call this test.jsx or spec. Either of those fine with me, I think I called them spec in the past, I'm gonna call it test this time.
[00:09:59]
Just go with what the mood suits. We just wanna test the alt text renders. Imagine that we got slapped by a lawsuit or a violation for not being accessible enough because our images didn't have alt text. And we wanted to make sure we need all pizzas to have alt text on them and we're gonna write a test that ensures that.
[00:10:24]
I don't know, making stuff up here, but that's what we're gonna do.
>> Brian Holt: So we're gonna import { render } from "@testing-library/react";. We're gonna import { expect, test } from "vitest". And import Pizza from, as you might have guessed, '../pizza';, okay? Now we're gonna go in and we're gonna say test("").
[00:10:53]
This string that we put in here, I don't really care what you put in. Some people have very strong opinions of how you structure these. This is my only, strong tip to give you here. I just want you to put something in here that if it says, this test failed, that you know which test failed.
[00:11:12]
So ("alt text renders on image"). That's what I have here, or on Pizza images, that's probably even better. It will tell you that it failed in Pizza.test.jsx. But just make sure that this is descriptive of what you're actually testing and at a glance, you can tell what failed if it tells you that this failed.
[00:11:37]
Okay, we're gonna make this an async function.
>> Brian Holt: And we're gonna say const name = "My favorite Pizza";. Const src =, I just put in a placeholder one, picsum is one that I've been using lately, .photos/200. I think if I just open it here I can show you.
[00:12:03]
Should just be a random image, right?
>> Brian Holt: So that's what it is today. Okay, const screen = render(. And you just render out whatever you wanna test, <Pizza name = (name) description,
>> Brian Holt: ="super cool pizza", and image=(src).
>> Brian Holt: Okay, const img = screen.getByRole("img");, and then we just write some expectations here.
[00:12:54]
Expect(img.source).toBe(src), and expect (img.alt).toBe(name);.
>> Brian Holt: Very simple test here, but kinda want you to just easy into a little bit of what testing looks like with the vitest. Render, this works like the root.render that we have from React DOM. But this is all being done in Node.js synthetically, so it's being much faster than it could be done in the browser.
[00:13:36]
So we render that here, we get this thing called screen. This comes from React Testing Library, which is a render result, and it has a bunch of functions available to it. But one of them is this getByRoll. I don't know if you noticed, but when I said screen., there's a lot of methods on this.
[00:13:56]
There's ways to get stuff out of whatever you're looking for. And they do this kind of interesting way of testing. We all wanna do a jQuery style where it's like, hey, give me this class or this id or something like that. And sometimes it's still kinda long for those days because it makes a lot of sense for me to do it that way.
[00:14:18]
But let me tell you why, I think theirs is better, even if it makes me think a little bit harder, Lord knows I don't wanna think that hard. It's very user-based, right? It's very based on how is the person perceiving this, how are they using it, as opposed to as me, the developer, what's the CSS class?
[00:14:37]
The user does not care if the CSS class is working or not, they do care if they can see the div or the image or not. Does that make sense? So they kind of change the focus around. In this case, getByRole, image is a role, right, it's a thing that user would experience.
[00:14:54]
So in this case, we know Pizza only has one image, so we can just say getByRole("img"); and be confident that that's gonna be the only thing there, oops.
>> Brian Holt: So that's why this works, but there's a bunch of them, let's see if I can even show you. Yes, there's a bunch of things here.
[00:15:13]
Get the fee, get the form, get the heading. The heading will be the h two, or the h one of it, or something like that. But it's interesting, these are all things that they're very focused on, what would a user feel here, what would they experience, and then you do it that way.
[00:15:29]
I'll show you later how to do it with test id, which is kind of approaching more what we want, is like, hey, I just wanna get this thing. I don't know what role it's gonna be, but I want this thing, I'll show you how to do that. Okay, so that is the most basic test.
[00:15:45]
Let's go ahead and see if it works. So I'm just gonna say npm run test, or if you're feeling extra lazy, npm test does work, or if you're feeling yet the laziest npm t works, specifically just for test.
>> Brian Holt: Lo and behold, we have written a test that works.
[00:16:10]
Now, I am always extremely suspicious when my test passed the first time that I write them, that I probably effed it up somewhere, right? So when that happens, on the rare occasions it does, I should go break this to make sure it actually breaks, right? So let's go break it, breaking things is fun.
[00:16:26]
We want alt text={"lol no"}, right? And now, if I look at my test now, this was expected to be this, but I was expecting it to be "My favourite Pizza", and instead, I received "lol no", right? So what we were expecting, and now if I go back and I fix it again, Pizza.test, or sorry, not that, Pizza.jxs
>> Brian Holt: Save it, lo and behold, it is working.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops