Check out a free preview of the full JavaScript Testing Practices and Principles course
The "Using Jest Mock" Lesson is part of the full, JavaScript Testing Practices and Principles course featured in this preview video. Here's what you'd learn in this lesson:
Kent codes the solution using jest.mock, which allows the user to mock an entire module to avoid monkey patching module exports. Then Kent demonstrates another exercise that allows the user to apply the mock in almost all of the tests, rather than having it isolated.
Transcript from the "Using Jest Mock" Lesson
[00:00:00]
>> Kent C Dodds: All right cool let's get this under way. So I'm just gonna uncomment those. The alternate to the module is just what we're using for the import and then the function that returns the mock object. So, this you think about this function as an entirely new module. So what would you put inside of your module?
[00:00:24]
That is what this function is. Except instead of module.exports, in CommonJS or export default, whatever, you're going to return, cuz it's a function, not a module. But for all intents and purposes, the way that jest is going to treat it and the way that you can treat it is, as if it's a regular module.
[00:00:42]
And so, what we're doing is we're creating a mock of the entire utels module. So, rather than, so whenever any part of our code goes to input this module because we told just we want to mock that module. It's actually going to swap out whatever import there is with what we are returning here.
[00:01:04]
I can do this because it just actually takes control over the module system and node. And so whenever you do a required statement or input stateiment, any of that, it's actually going to go through just first. And just will check it's registry of marks and if there is a mark available for what you are requiring then it will use your mock instead.
[00:01:25]
And there are various APIs in jest to say for this case don't use the mock, use the actual implementation, and things like that. And then jest does some magic stuff to make sure that it all works as you would expect, despite the fact that this code is actually running first.
[00:01:43]
And so there is some cool magic stuff that jest does under the hood. I don't think we really need to get into that, just suffice it to say that creating a mock like this allows us to mock an entire module. So here we just want to expose the getWinner function.
[00:02:02]
And rather than the spyOn, we can use jest.function to create a mock function and then we can actually pass our implementation here as well. So this getWinner is gonna be a jest function that is implemented like this. It's gonna keep track of when it's called and how it's called.
[00:02:23]
And now from that we actually no longer need this because utils.getwinner is now going to be this function. So we can get rid of this entirely. And we don't need to have this anymore either. We don't need to restore it. All of our test are going to be using the get winner mock function and so when you save this and our test ran so fast we didn't notice but yeah, they're still passing.
[00:02:52]
And there's one thing I wanna mention about this, but I'm gonna stop really quick for any questions people have.
>> Kent C Dodds: Yeah.
>> Student: So this mock is replacing the util is important in this file. So if we import util's in another file and don't lock it. It wouldn't be overwritten by this.
[00:03:14]
>> Kent C Dodds: Yeah, that's right. So, this mock only applies for this test, for the test in this file. So if you made another test somewhere else and actually, in fact we did, this util is actually going to be the actual one.
>> Student: Sure, and then is there a way to override just certain-
[00:03:30]
>> Kent C Dodds: Properties? Yeah, I'm glad that you asked. So if we want to get the actual version of the utils and then just override certain properties as you were saying, in our case there's only one property, but so we'd say actualutils, whatever you want to call it. And then require .requireActual.
[00:03:51]
So requireActual is coming from jest, that's not like a node thing. So Jest Monkey Patches require actual actual entre require. Because actually, the require we're using is jest require function anyway. But then you'd say the relative path utils and then what I would do is I'd just say, I want all of the things except this one I'm gonna override.
[00:04:18]
>> Student: Cool.
>> Kent C Dodds: That's a great question. So one other thing. Because we have this test and it's calling Thumb war. If we wanted to have another test and then assert that getWinner was called twice. Actually, here, we can do this. So let's just copy-paste returns winner again. We'll save it, we're gonna get an error because it's now called four times instead of twice.
[00:04:45]
That's because we ran both of these tests. You want your tests to be totally. To work in total isolation of each other. You should be able to run only one if we skip here, .skip. Then it's gonna work. So you want to make it so you can delete a test and it doesn't impact any of your other tests.
[00:05:03]
They should all work in total isolation of each other. So to make this work, there are a couple things we could do. First we could start with utils.getWinner.mockClear. And that'll just clear out all the state of the mock. And we'll do that before each of our tests. So that would make it work.
[00:05:20]
Because we'll probably be doing this in every single one of the tests in this file, this is a good use case for, before each.
>> Kent C Dodds: Oops yeah and we'll just put that in there and then we don't need to we don't need to do that in every one of our test.
[00:05:40]
>> Kent C Dodds: Cool so there's one more thing I don't think we're gonna do it as an exercise I'll just show it to you it'll be kind of hard to do as an exercise anyway. But does anybody have any questions before we do that?
>> Student: I just had one more, related to sort of you showing a case where two tests success are based upon each other, is there a way jest to run tests in a random order?
[00:06:11]
>> Kent C Dodds: All right, that's an interesting question. So, in tests within a single file. As far as I know, there is not a way to make it run in a random order. It does run all of your test files in parallel in random orders. In fact, jest is so cool that if one test file has a failing test and then you make a change.
[00:06:31]
And it's going to run that test file first, when it reruns all of your tests so that you get that feedback very quickly. Which is like just as all of these little features like wow that is really handy. But yeah, as far as within a single test there is no way to do that as far as I know.
[00:06:48]
It sounds like a pretty cool pull request, though. Okay, so let's go ahead, and you don't have to do this exercise because it's pretty straightforward. So as was noted, actually I think Peter mentioned this, this mock will only be picked up by this test file, and all modules used during the lifetime of this test module.
[00:07:13]
What if you have a module that you pretty much never want to have, like you're gonna be marking in almost all of your tests. So in that situation it's nice to be able to isolate that to one file and have the mark for this module be in that one file forever.
[00:07:32]
So that's what this last en is all about and actually I already have it for you. The way that that works and this is conventioned by jest. I don't think there's a way to change its convention but you have your mask directory right next to the module that you wanna mark.
[00:07:47]
And inside of that you give that the same name as the thing that you wanna mark so that name is important. And then that is a module. So anytime that module is required, this is the one that it's going to be pulling in rather than the actual implementation.
[00:08:06]
So that's all that is. So if we were to update. Let's see. Update 5.todo. All we do is remove this but then we have to instruct jest that we, yeah we do wanna pick up the mock so we say jest.mock. The relative path to the module utils and then because we're not providing a mock function like a mock module function thing it's gonna say you're not providing one, I'm gonna go see if there is one.
[00:08:31]
If there's not then I think it blows up but in our case there is one so, we should be good, thumb-war.5.todo. There we go. So it's picking up our module in the mocks directory. So one other thing about this is often, the thing that you want to mock, is going to be something in your node modules directory.
[00:08:57]
So you're going to mock axios, for example, it's a pretty common case. I actually have some additional bonus exercises for mocks. That you can find in utils. And let's see it's in jest here so we want to mock axios. So we're not actually creating jest and stuff. So the way that works is you put your mock directory at the root wherever your jest configuration is set to have the root set out, and you put your mocks in there.
[00:09:29]
And then those will be actually be picked up automatically. So if it's in the known modules directory, those, any mocks for those will be picked up automatically. I guess jest figures that's like the common case if you have a known module that you have a mock for, you probably always want to use it.
[00:09:46]
If you don't want that, then you can say adjust.unmock and then the name of the module. And now, it will give me the actual, like, this important now, is the actual module. But again, that's lot of this is kind of technology specific. But more the general idea is the optimal mocking strategy is to be able to mock an entire module, rather than monkey patching.
[00:10:12]
While that's still, it works and it kinda makes sense, being able to mock the entire module is generally preferable for various reasons. And in jest you accomplish that with this mock directory or the jest.mock API. Yeah?
>> Student2: Something we've come up against is, say we have an API that we're mocking and we actually want to mock different responses test by test And so if we import the api module and then you know mock it like this we seem to have a hard time.
[00:10:53]
Let's see what's the actual thing that's going on. Okay, so what actually happens is say we're. Importing a module that relies on the API module. So we import but inside that module that we're reporting that then uses the API module, it imports once, right?
>> Kent C Dodds: Mm-hm.
>> Student2: So we'll mark that at the top of our test file.
[00:11:20]
But then it wont actually be used in the module that we're importing, that depends on the API. So we'll always ended up having to use, require the API module repeatedly in each test. To overwrite it, is there a better way we could be doing that?
>> Kent C Dodds: Yeah, so, let me first make sure I understand what you're saying, so you are using just the jest mock API, is that right?
[00:11:46]
>> Student2: Yes.
>> Kent C Dodds: Okay, yeah. So what's happening, or what you're saying is like within a single task you wanna have different mocks for, yeah, for this API, because you want to have the responses different. So there are a lot of ways to approach that. In general what I trying to think if I have an example for you.
[00:12:10]
I think maybe the client mock might. Yeah so what I do is in this example and this was in something that I wasn't I was planning on showing you. So this may get a little bit confusing. I expose a mock property on the export. And this mock property has a reset function.
[00:12:30]
And so you could expose any number of functions that swap the Like object assign certain properties or certain functions and override those. You can also rather than swapping the entire implementation if it's just one of those functions that you're trying to change how that implementation works you can also do just dot or like turn that into a mock function and then mock implementation.
[00:12:58]
But if it really is the entire module, let's see there was, I think I actually just wrote a blog post about this. I've got a newsletter and this last week's article is about pure modules and why it's a good idea. One of the reasons is for test ability.
[00:13:20]
As an example of a situation where you might need to do what you're suggesting. Here we have this C module and at the root level of that module it's doing some work. So in general you want to avoid that for various reasons. One of them is test ability.
[00:13:36]
But if you need to reset this module, have it rerun itself again. Then jest has a thing for that. Let's see. It's the jest objects. jest.resetAllModules.
>> Student2: So that's what we've been doing-
>> Kent C Dodds: So you are doing that, and then you require. Yep, that's pretty much the way to do it.
[00:13:59]
>> Student2: Okay.
>> Kent C Dodds: Yeah, it's kind of annoying but if you have pure modules then it's a lot easier to get avoid having to do that. So pure module being like it's just a module that exports some. Functions, doesn't do anything at the top level, if all of your modules are that way except for the one that kicks everything off, then you won't normally need to do this reset module thing.
[00:14:24]
And if you are then you could probably find something else to do. But yeah, if you are doing stuff at the root level, then yeah this reset module is the way to go.
>> Student: I didn't think we were thinking of moving towards what you showed actually in the mock up jpg reset method.
[00:14:40]
You know reset method, you know it was cool seeing that because that was what we were thinking of doing.
>> Kent C Dodds: Yeah yeah I do use that and it works fine. And you can just stick that in there before each and yeah and it works fine.
>> Student: Thanks.
>> Kent C Dodds: All right, cool.
[00:14:58]
Any other questions? All right, yeah?
>> Student3: Yeah, I was just gonna say it, I mean do you use mocking a lot because it seems like this is a lot of work, recreating modules. And so forth.
>> Kent C Dodds: Yes, that's a great question. I do try to avoid mocking. The guiding principle for how I think about testing the closer your tests resemble the way your software the more compensate can give you.
[00:15:29]
When users use your software they do not mock anything. And even software users, the consumers of your APIs. They don't mock things. And also when you mock, you sever any confidence that you have that those things are working together properly. So you have to have additional tests, like contract tests, to make sure that that connection is working.
[00:15:50]
Which is fine, and like there are use cases for it. But yeah, if I can I try to avoid mocking. But yeah, sometimes you can't avoid it.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops