This course has been updated! We now recommend you take the Introduction to Node.js, v3 course.
Transcript from the "Jest" Lesson
>> And yes, it's not bad. I mean, you could go ahead and write a bunch of unit tests for a bunch of code with this. But I don't know if you would want to because one, the reporting mechanism is not that easy to read. Can you imagine if you had like 20 tests fail, and this was just like what you pulled open times 20 inside of a log file on your server somewhere like this would be terrible, right and the CI or GitHub.
[00:00:23] You wouldn't want to see this be really hard to parse through. So the reporting is bad. And it just really wasn't that flexible. What if we, like right now, I don't have like any formatting up here about what I'm actually testing, what piece of unit and then what I'm testing that unit four.
[00:00:41] I logged it here so you can see it, but it doesn't really stand out so it's really hard to see. And I don't really get any indication of like, how many tests I ran, how long the test took to ran, how many failed, how many passed just like the statistics on that.
[00:00:54] That's not saying we can't make it, using the assert lib module. We could totally make all that stuff. But again, why would you reinvent the wheel when there's already good stuff out there? So we're going to use one of those things, and there's so many of them and they pretty much all have been piggybacking on a really popular one called Jasmine.
[00:01:38] So let's do that, let's install some jest. So we'll say npn install jest but we're gonna say save.dev and not save. That's because jest is gonna be a dev dependency. So a dev dependency is exactly like a dependency. But it's meant to be for development as in. It is not your app does not depend on jest being installed for it to operate.
[00:02:05] If jest was not installed in this application, the ad function would still do what it's supposed to do because it doesn't import jest. It doesn't use jest. Jest consumes our app so therefore it's a dev dependency. When our app is executed by the node interpreter, it's not gonna include jest, it doesn't need it.
[00:02:22] So it's a dev dependency. And the reason that's important is because, when you deploy or you publish to NPM, and someone's pulling in your code, they don't need your dev dependencies. Because they're never going to run tests. They're never gonna run the build script. It should already been built, and you should have already tested it.
[00:02:38] So you don't want to ship those extra dependencies on someone's machine. Same thing for deploying, if you already built inside of a CI or something like that, you don't need a lot of those dependencies to be sitting up there. So don't even install them. So dev dependencies just basically give you another Boolean to install lines, so you can tell NPM.
[00:02:55] Install jest the dependencies or install just the dev dependencies or install all the dependencies. So it really just depends how you want to do it. It's just another flag that you can basically Boolean off on when you do an install. All right, so now that we have jest, we can switch this over.
[00:03:13] Actually, we'll leave this one and we'll just write some better tests that we can kind of compare, so we'll write some more code here. I'm going to open up a new file, and I want to call this one utils. And I'm actually going to do a js here.
[00:03:32] I know we said we're going to mjs this whole time, so this might be confusing. But as of right now, the current version of jest does not support mjs files out of the box without some type of configuration that I think is a little out of scope of this exercise.
[00:03:46] So I don't want to cover that. But the next version of jest that you can install but just beta does support it with some small configuration, but also didn't want to inflate and confuse anyone with that configuration. So we're just gonna just switch to js and use common js, for the course of jest.
[00:04:03] But that's not going to determine how you use jest is still gonna work exactly the same. It's just determining what type of modules we can use. So we will be using common js here for Jess, so bear with me. I do have some code though, that I want to test so we're gonna go grab that code.
[00:04:19] It's really not doing anything and it is this code right here. So here's the code that we're gonna test. I'm literally just gonna copy and paste it because this is not the important part. The important part is the test that we're writing. So I'm gonna paste that in here.
[00:04:34] And just to go through it, so we kind of know what we're doing. I have a delay function up here that I'm using just a simulate asynchronous program. We won't be testing that, have a users array that's basically the database. And then the two functions that we will be testing will be to get new user function, which is a synchronous or asynchronous function that queries the database for a user that has this ID.
[00:04:56] And it tries to, if it doesn't see that user or throw an error, if it does, it'll return to user. The other function that we will be testing is gonna be map object to array. It takes an object and a callback. And it basically maps over that object and puts all the results inside of an array.
[00:05:14] So those are two functions we will be testing with jest. So now I'm gonna make a new test file here and I'm gonna call this utils.tests or you can even say spec I like spec.js like that. And started by writing some tests. So to sweet about writing test with something like jest how we're going to execute this and that's really important because it means we don't really have to import a lot of stuff.
[00:05:45] So what I'm saying is, if we head over to the package JSON, which I don't have, I need to actually create that. So I'm just gonna say npm init like that, there we go. And what's crazy is that even though I created the package JSON later, after I already installed, you can see NPM was smart enough to add the dependencies that I had.
[00:06:08] Wasn't too smart to put just in the dependencies and not the dev dependencies. But there's no way you could have known that it was a dev dependency when it wasn't here already. So it kind of got there. I should have emitted before I installed but, it is what it is.
[00:06:22] So the sweet thing is we can just hijack this test command. That we just put just here like that. And because we're executing the jests tests and the jests environment with the jest CLI we don't have to actually import any of the stuff that jest has. It's just injected to us globally, because it's executed in a jest context, which is really cool.
[00:06:46] So what does that actually mean? Well, let's write some tests. So typically what you do when you write a test and something like jest is you import the code you want to test. So in this example, I want to import from utils. Sorry, I can't do that. We got to do this one.
[00:07:02] Got to do some require stuff. I'm gonna import from utils and I want to get NewUser and then map object to array, so we got that. And then what you would normally do here is you would do something called describe, and you can see I tried to autocomplete from yards.
[00:07:22] I don't want the yards one, I want the jest one. And typically the way described is is like you're describing the piece of code that you're about to test in this case. Let's test the map object to array first. So 'mapObjectToArray. And I'm gonna do that to let you know that it's a function.
[00:07:41] So we're gonna test that, this is what we're describing. And then each thing that we want to test, each assertion if I must rewrite a test function for it not to be confused with. You see it tried to include the test global from jest which is the same thing, but we don't need to do that because it's injected for us.
[00:08:02] So now we can say test. And then I can actually write like, what do I want to test? Well, let's look at the code. I want to make sure that this puts the, I'm gonna make sure that it calls, now I want to make sure that it actually just does what is supposed to do.
[00:08:21] It returns an array of all the map values. So I'm going to pass it something and then I'm going to see what the output is. And if it doesn't match, it didn't do its job. So either the code is wrong or my test is wrong, and we had to figure it out.
[00:08:32] So I'm going to test that it maps values to array using callback. So that's what we're going to test, and now what we can do is we can say results=mapObjectToArray. I'm going to pass in an object and we'll call it, this is a food order. So, where's there a shake, yes.
[00:09:00] Did we have fries and were they good, no. And was it fast, yes. So we have these objects here, we want to map them to an array. So if my code is doing what I think it's doing well, I guess now I got to pass it a callback equals to that.
[00:09:21] Let's do that was, so a call back over here. So if we do a call back, I can take each one of these should be like a k and a v here a key and a value. And I'll just basically, actually, this is not a good one. Let's do some numbers here.
[00:09:41] Numbers will be better to test, let's say age:30, height:65 whatever. So we got that, so now if I map over these and I say return V* or V+10, there we go. So I should get back an array of two things. And it should say 40 and 75. So that's what I expect.
[00:10:07] So I'm going to say expect which is another global from jest. You can see I try to auto import I don't need it. So I can say expect(results). Then I can say toEqual and I want to do equal because I'm about to compare two arrays that are two different arrays in memory we have the same value.
[00:10:29] So toEqual is a deep equality check and not a exact match. I want to do an exact match I would say toBe. And that would say is this array exactly this array and that's not what we want, we just want to make sure they have the same values.
[00:10:42] So I'm going to say it should be an array of ([40, 75]). So let's try that, so now what we can do is we can run the npm tests, and I think we're gonna get an issue because we have this other test file here. Let's get rid of this right quick so it doesn't interfere.
[00:11:05] I'll just rename it and just not put a speck in there, there we go. So now I can just say npm test. And you can see jest says cool this pass this file. Here's what you were testing. Here's the actual test. Check mark means it passed, 1 passed of the all the test suites, 1 total, total tests, snapshots of already snapshots.
[00:11:31] What we're not doing, and just how long it took in general. And then it ran all test suites. So as you can see, the output is this a lot better. If you can imagine just a bunch of these with green checkmarks on them, or red X if they failed.
[00:11:43] This is a lot easier to see and visualize than anything else. So it's not that like is doing anything different. It's just one it gives us a better output. And two, it's a little easier to develop with in my opinion. Although asserts really not that bad is just, there's a lot of setup here that I had to do.
[00:12:02] Whereas jest, yeah, there's a lot of ceremony with the describe in the test. I think it's necessary ceremony, it kind of works. So that's how you would write just a basic test here, we can move on to doing something like a spy. So for instance, we know that we have to pass in a callback here, but I want to test to make sure that the callback actually gets called.
[00:12:22] I'm not sure that if it gets called I want to make sure that it does, so callback gets called, so we'll do this. And we'll just copy this right here. But before, I'm not gonna actually add this callback here. I don't really care what the callback does. I'm just gonna call this a mock callback and I'm gonna make that.
[00:12:46] So, const mockCb = jest.fn. So that's just gonna create a mock callback. And as you see, I'm using this jest keyword here, that's also injected into the global space because we're running it into adjust runtimes, pretty amazing. So now I can say, expect(mockCb).to). It's like expect(mockCb). What is it, I can't remember, I have it here.
[00:13:21] In my test, what we want to see, yeah, mockCb.mock.calls, there we go. Sorry to keep up with, so that (mock.calls.length).toBe, well, I know this callback is going to run for as many key value pairs as in this object right now. There's 2, so it should be 2, should have been called twice.
[00:13:47] So, I'm gonna check that out. To see if that works. Let's make this fail so we can see what happens. So let's make that fail. And you can see right there, we got a fail on mapObjectsToArray, callback gets called that failed. Expected 2, I'm sorry, expected 33, but received 2.
[00:14:10] And you can see where it colors out, so here's the expected and here's what it actually got. And even points to like where you wrote the code, and then you get all this really cool stuff. So it's really great how this works. So now you will go back like well, no, this test is right you should have been caught 33 times.
[00:14:26] So now the code is wrong, but more realistically, it looks like my test is wrong. So obviously I will change that to 2 and now my test will work. So now we get all these green check marks, which is like the best feeling. It's so good when you do a pull request and the CI says you're good to go to all those green check marks.
[00:14:46] It feels so good because then like, you'll be out. You'll like, do a PR and then you'll walk away and you'll go like fix a grilled cheese. Then you look at your slack from the bot that messaged you and it says, your test failed. And you're just like dang, I can't even eat this grilled cheese now cuz no one was gonna hit you up.
[00:15:02] You got to do a rollback or whatever. So it was really nice to see those green checkmarks