Check out a free preview of the full Enterprise UI Development: Testing & Code Quality course:
The "Setup Unit Testing with Vitest" Lesson is part of the full, Enterprise UI Development: Testing & Code Quality course featured in this preview video. Here's what you'd learn in this lesson:

Steve introduces Vitest and provides a basic unit test example and a description of the Vitest syntax. Instructions for running tests in the course repository are provided. A question about using Happy DOM vs. JS DOM is also covered in this lesson.

Get Unlimited Access Now

Transcript from the "Setup Unit Testing with Vitest" Lesson

[00:00:00]
>> With that said, we could start from the playwright test and work our way down. It seems like just for our own happiness, that we'll kinda start at the bottom with unit test and work our way up. Again, we'll talk about unit testing. We're not gonna go all the way into the weeds.

[00:00:17] We're not gonna do the thing where are like, let's write our own testing framework. We're gonna kinda like run a few simple ones to make sure that everyone's setup works and that we're all on the same page with, like, Vitest and stuff like that. And then we'll kind of talk about some of the more advanced techniques that have been helpful to me when refactoring code.

[00:00:37] And then use that as our first foray into building up a build process that runs the aforementioned tests, and make sure everything works the way that we think it does. If you have never seen a unit test before, or you just wanna make sure that we're on the same page, this is a pretty good example.

[00:00:58] We have a completely worthless function called add. It takes two arguments and it uses the plus sign, there's probably a lot more tests that we would have to write because JavaScript as you know, two plus two equals two. The string one plus one equals eleven. I don't even remember what happens in the other way, because I don't have to, because I use TypeScript and I know that a and b will be numbers and I know that it will return a number.

[00:01:24] So an entire set of tests that I don't have to write anymore. But I can have this ridiculous one down here, which is it should correctly add two numbers. Clearly there's nothing checking for typos. But we have an expect statement that exercises the function, and then we verify that it is the answer that we expect.

[00:01:47] You could use assert. There are various different ways to expect or assert that things are what you think they are. They don't really matter that much, whichever one makes you happiest. I liked to assert for longest time, but I lost that war because nobody agrees with me. An assertion is just basically, or expectation is like, hey, I'm expecting this if it's not, throw an error.

[00:02:09] All, and I'm gonna repeat this about three more times over our time together, all a test runner does is runs your code. An assert or an expectation or expect throws an error if it's not what you expect. All the test does is collect those errors and tell you about them at the end, instead of blowing up after the first one.

[00:02:29] That's all a unit test runner is. You could write on yourself, you shouldn't, you could. Test is just making the code do things like I said, the test runner keeps track of it all. A test suite is just a pretentious way to refer to a JavaScript file with a bunch of tests in it that runs through it all and will know.

[00:02:49] Awesome. So what we're gonna do now is we're gonna go and we're gonna just, run some tests and see some and just look at the tool that we're gonna be using for both unit testing and component testing. One if you've never used Vitest test or even Jest before, maybe you're using Mocha, just to make sure that, we're all on the same wavelength.

[00:03:10] We'll go take a look at the tests and run some and give it a sense. If you try to run just a testing note on the repo. If you try to run npm test from at very top level, it will yell at you. You just need to go into one of the examples and run it from there, and it should be good to go.

[00:03:32] So make sure you navigate into source examples, getting started and try out both npm test and npx vitest, which you can totally run from the top level just don't. And one what I would love in this quote unquote exercise is, if it's not working for you, we should deal with that now.

[00:03:54] Otherwise it gonna be a long day. So we'll take a look at that, but gonna run the tests and we'll take a look at a few of them and we'll talk about them. But let's make sure everything works before we get in too deep. So the question was, am I gonna use Happy DOM with vitest?

[00:04:10] So let me start by answering what Happy DOM is. Your tests run in Node, right? Things that Node does not have, a window object, a document object, anything that is in the browser, right? It's just running JavaScript. Happy DOM and it's older sibling, JSDOM are basically spec compliant implementations of the DOM API in Node.

[00:04:39] So then you can go document.body and you can deal with DOM nodes and stuff along those lines. And it will all just work even though you don't have a real DOM. And Happy DOM is a fast lightway, way of dealing with that. The origin story, for those of you who care, is I started out using HappyDOM, and then I had one edge case where Happy DOM didn't update whether or not a button was enabled or disabled.

[00:05:11] And I lost half a day of my life. On a whim, I switched out to JSDOM, and it worked. [LAUGH] So that informed it. I use Happy DOM at work, and I've never had a problem with it but in this workshop, it's gonna be JSDOM but other than one tiny edge case.

[00:05:26] You can use whichever one makes you happier, that was an unintended pun. But generally speaking, I'm gonna go with JSDOM because of one edge case at bitmain. But like that also informs, that tells you a little bit who I am as a person. All right, so going into source (src) But like spiritually, we'll use a DOM abstraction, just not happy dom for reasons and getting started.

[00:05:52] And then cool, open that up in code. And I should have just done the entire thing but here we are. And so in here I can run npm test. And it will run my tests right and we'll see everything like this. If this looks like Jest to you because you've used Jest, yes it does look like Jest.

[00:06:14] One thing to note is that it drops into this watch mode automatically. As soon as you run the tests, you can type in q to leave that or you just hit Ctrl C like everything in the terminal. You can hit h for a bunch of options that we'll kinda talk a little bit about later.

[00:06:27] But you can rerun all the tests, only the failed tests, snapshots which we'll talk about later. Filter by a file name or filter by a particular test name, if you're just working on one particular thing and you only want that to run. But generally speaking, with out of the box the one major difference between Vitest and Jest is that out of the box, you need to import the various things that you're using.

[00:06:57] So like describe, expect, test, it so on and so forth. If you hate that or if you are migrating from Jest and you don't wanna do that, you can literally, for your test suite, go in here and say, globals is true. And then all of those will now just be globally available like they are in Jest.

[00:07:21] Great, if you're migrating, great, if you just don't want to import those things all the time both are totally fine with me. They will be reasons why we have to have them globally available later. Particularly why I bring this up now, is particularly when it comes to like, remember how I said like you can just use like third party extensions that are meant for Jest?

[00:07:42] Well, there's certain things that they are expecting, unintended pun. But yeah, so if you want them globally available, that's how that works, and you can see it right then and there. Awesome, and so we've got all of this kind of in place. You can see some of these tests.

[00:07:59] One of the questions that will probably come up is what is the difference between it and test? Nothing. Which one should you use? I don't care. Which one do I use? I would love to tell you that my rule is if it's in a describe block, I use it, and if it's outside, I use test.

[00:08:17] But that's not even true, if I find myself writing sentences that start with it in the test description then I will use it, and if I find myself hating that, I will use test. They are the same thing, they are no different. Cool, one more thing is like, true to be true, then there's not to be which is does not equal.

[00:08:43] You can also expect that a test fails right, so this is like opposite day right where this test only passes if it fails. There are reasons why that will come in handy at certain points which I'll say now and then there's entire slides dedicated to this later. But I will say now which is and I said this before, a test passes when it does not fail,right?

[00:09:11] So if for instance, like you completely commented this out, well, that still fails. That was a bad example. This one, it still passes,right? And because that's under the hood how a test suite works, which is if this is not the case it throws an error, all right. And that's what fails a test, the absence of a failure is passing.

[00:09:34] There are some times when we get to component testing and browser testing, integration testing, if you will, well that's great because it's like, hey, go find these buttons and click them. If you found them all, do I need to expect anything at the end? I can, but like you found them all versus if that button wasn't on the page or that like div wasn't there or something along those lines and you blew up and failed the test like cool, right?

[00:10:01] Like sometimes the absence of a failure is all I need for the pass but sometimes we need to fail a test to make sure that they are actually testing the thing that we want or are they just passing by accident. So .fails or throw in a not in there's sometimes it's helpful just to make sure that we don't have a false positive.

[00:10:21] One of the nice things about Vitest is I don't even have to have a section. We have like a minor section on like edge cases. But I have a section of like, how do you deal with asynchronous code? Like if it returns a promise like it all just works using async await functions living in 2023 or whenever you have the watching this post 2023.

[00:10:42] Is great because async is not nearly as hard as it was when I was a kid. And so you can use async await functions. The one difference I noticed is that a lot of older testing frameworks would let you put done in here, and then call it when your callback was done to say that the test is completed.

[00:11:02] Jest doesn't support that, it actually takes a different argument in there. If you really had a bunch of callbacks, you probably wanna give yourself some kind of abstraction like this. That said, I wrote this test for this workshop. This is not a thing that I've ever actually come across in the wild.

[00:11:18] But I also don't have to deal with old callback code I mostly have promises to work with, Mark.
>> Is there a reason to use Vitest over Jest if you're not already using Vite? I know Vitest works better with the ES modules.
>> Yeah, that is basically the answer.

[00:11:37] I would say that my heuristic is like, so for instance, Jest comes built in to create React app, right? Which I understand is no longer the canonical way to install React, but let's not get into that right now. I will use Jest if it's there. I would say that if you're using React app and you're using Jest, that's totally cool.

[00:11:53] If you're using Vite, it makes sense to use Vitest. They are in only tiny ways different. I think that Vite is faster, right? For us, I work these days. Our marketing site, a bunch of those sites, are using React, but like the core application is in Svelte which uses Vite.

[00:12:12] So, originally used Jest and I was fighting with common js versus the Es modules but I'm done fighting with that so I migrated it. But that migration also took me 90 minutes, it did not take all that long. So yeah, I would say that there's like, and if you're using like Webpack and you're using Jest already, I think that's totally fine, right?

[00:12:39] If you're not and you're using Vite well, yeah, having something that integrates the Vite makes more sense, allegedly is faster, but that's always like if that could change next week. There's also runIF and skipIf, which will take some kind of condition and will do what you think it does in those situations.

[00:13:05] Generally speaking, I would say that these are probably a little bit more useful for the node developers among us who might have to deal with some of the intricacies of running on Windows. Generally speaking, they are here, you might need them one day. I build my application for both a SaaS platform and open source, so like there's a world where maybe I will need one of these one day.

[00:13:35] They are here for you they are there but generally speaking you probably don't need them. Cool like I said like you'll notice that like it will run basically all the tests in a folder. Out of the box, it is looking for anything with .test.ts. You can change that to .spec, you can change it to whatever you want.

[00:14:00] You can give it a bunch of pattern matches for include and exclude. I will use that to dramatic effect to skip solutions if I want to or something along those lines or have a different configuration working for the Svelte app. But generally speaking, it will run everything with .test.ts or anything in an __test__ directory.

[00:14:25] If you want my personal opinion, because you asked or somebody was about to. I think there are two schools of thought. I have one test directory where you put in all your tests that are divorced from the implementation. I am a big fan of whatever that file name is .ts, that file name .test.ts, right?

[00:14:46] And just immediately having it right next to the same file. I don't want to traverse the file system looking for things. I can't handle that.