Mocking Asynchronous Functions with Jest

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome to swashbuckling with code i'm jimmy cleveland we're going to be covering mocking asynchronous functions with jest in this video so mocking is a fundamental skill in testing for those of you who aren't familiar with it and in this case it means to imitate and not to insult so what mocking does is it allows you to avoid testing parts of your code that are outside of your control or to get reliable return values for that code and one of the situations that's really desirable for mocking is api requests such as with axios and so that's what we're going to cover in this video now i have an accompanying blog post that i wrote some time back that you can use for a reference material if you'd rather read that than watch the video if that's your flavor or if you just want to look at it afterward it's on blog.jpg.com which i'll link in the description in case you want to check that out but let's get to some mocking now let's start out with a brief overview of the project just so you know what's going on in it i've just thrown together with an npm in there really quickly and then we have a test script that's just going to run jest and you'll notice that there's the suggest key here that's a way that you can make a setup file and i'll show you that in a moment that's just for regenerator run time for me so that i can use the sync weight and then we have axios and just and axios is one of the things that we're going to be mocking to show that then obviously we have jest and then we're going to have our babel core preset env and then that regenerator runtime okay now what you're going to want to take a look at real quick is this setup test to show that it just has this import regenerator runtime that's just a way that you can add like any sort of functionality that you want just to pick up and i'd probably do this a little bit differently in a full project but this is a pretty lean one just for an example so that you can see that we have this babel file that just has preset a and b just so you know nothing's crazy is going on there and that's pretty much it the index we don't even need to really look at so we have this swappy getter which is what we're looking to test here and i'm going to be doing this side by side here so that you can see the the the test running the entire time so it'll be a little bit squished uh sorry about that but you know we'll make do i think it'll be okay so we have this swappy getter and what its functionality is is that it's going to import axios and then it'll export this function swappy getter and it's going to hit the swappy api the star wars api if you're not familiar with it you can use it it's pretty cool here's the url here and you know i'll probably link it in the description just in case you haven't seen it it's a really nice little public api that lets you just kind of test out you know hitting things and getting data back so in this case we're going to look for people by id that you pass in we're going to return i happen to know that it's res.data.name when it comes back so i'm just trying to get back the name it's a really simple contrived example but i think it'll it'll serve us for what we need so the first thing we're going to want to do is i guess show that if i run npm oops npm run test you'll see that we get this no test found and you can actually do npmt for that just so that you know and then you can also use a dash dash to pass on additional arguments and then we're going to do dash dash watch in this instance and so what that will do is start up our just watch command you could a lot of people will do this make it so that your jest just automatically starts out in watch mode but typically i run this in ci and i just use the same command so i'm used to just passing the watch flag so that's how i do it you know you do you you do how you like so what we're going to do is we're going to add a new file and i'm going to do swappygetter.test.js that will immediately update it's pretty cool and it'll tell you that you've got to have at least one test so okay so let's start out here um i like to do mine with the describe block i know some people aren't really a fan of the describe block anymore but uh you know i still like it i think it's cool to kind of group things but if someone has pretty good arguments against it i'd like to hear it so then we're going to run this uh i'm just using a little snippet library i have here and i actually probably should enlarge that so you can see that and then we're just going to say should return a name and so that's going to be our test and then now we'll have our first test that doesn't do anything and it's just you know successful right nothing crazy and then what we'll do is we'll import swappy getter from that directory swappy getter now because i'm just using babel i have to add the js extension you might be used to not having to do that just so you know and stop formatting that and then what i want here is i want to say const result equals and then we're going to call that function swappy getter and then let's just log the result so that we can keep track of that so you can see what's going on and then i'm going to say expect is a pretty standard test expect the result we'll say 2 b and then i'll just type the name in here so this case would be like luke skywalker because it's number one and you'll see what we get here on the left isn't this watch mode cool it's just kind of like live updating as we test it so we're getting this received an empty object and that is because our swappy getter just to take a look at that code again it's returning this axios.get with a then returning it so we're essentially getting a promise back but we're not utilizing that yet we're just trying to call it now i know this is pretty obvious to most people but i do see quite a lot when people are getting into mocks especially for async stuff like when i've been looking up stack overflow and stuff i see often people thinking that their mock is not working right because they'll see received undefined here or they'll see just an empty object or something like that and i just wanted to point out that that is what you're going to get if you're not even doing the problems correctly in the first place so maybe look for that if you ever run into that situation okay now i realize we're not even calling our our function correctly here right it takes a a number so we might as well just pass that and say yeah it's still the same it's still the same problem now you could use promise chaining here and testing i really prefer a sink away i'm usually kind of 50 50 on either but in testing it's just a lot easier in my experience to use the sync away so that's what we're going to do and that's why if you recall well let's actually look at that real quick uh you'll see that this test passed now pretty neat huh and then save with you know make sure that it actually fails and it does it'll say expected loose guy and now i got luke skywalker so what i was just saying was that remember in my setup tests i have this oops i've got this regenerator runtime and that's why i can use the sync away here in this case i just want to point that out so you know a lot of situations you'll be able to do that in your project already but that's why and how i have mine set up so okay cool you know we have a an asynchronous test working here but uh you know the there's kind of an issue here that i want to talk about before i show you the mocking and that's kind of the why of it the issue here is that i'm actually hitting an api so this is supposed to be a unit test for me it's obviously it's a contrived example you know you'd probably have something doing something and then um you know the the api you're hitting is just a side effect of that that happens to go into it but what i'm really trying to do is i'm trying to test this unit not this integration and where the problem commonly arises is that let's say um the api is down for one and now my tests fail but my code's actually still working properly the api just happens to be down or what if the api changes out from under me you know and i get different results back then i get false positives and i think someone committed some different code that kind of messed my unit test up it's not just a temporary blip in the api or an update or something like that so these are some of the few reasons to get into mocks because really what you want to do is you want to test your code in isolation so that you know you're not relying on any sort of side effects here so let me kind of show you what that looks like so i can do a jest.mock and since i'm in a test file i can just call jest here it'll you know be in the environment when the test is running and i can actually just type axios and you'll see that that fails but what it says is cannot read property then of undefined so what happens when you do just.mock so that you know is there's actually a thing called just.fn where you can create a mock function i'm just it's a pretend function but it has some additional functionality from as loud as saying function has a lot of functionality from jess that's allowing that let me show this real quick if i go to swappy getter i can log this is where my test works i can log axios and you'll see that uh i have to scroll up a little bit here um but when it does it you'll see it has this request get uri delete get there's our get method and all that stuff so it's the actual function but check this out when i do just.moc it's kind of interesting huh you get this big long one wow it goes far uh it goes all the way up here and you can see this is mock function as true and then it has a bunch of methods on it mock result value one smock return value we're actually going to use a couple of these in a moment so this is pretty neat and if you're a little bit newer to this this might kind of be weird to follow but what's happening here is our our away is or our test here the away is calling swappy getter now when we call that it actually calls this code here and so we are running through all of this code and so i'm logging out axios but because i import axios in that module this is swapping it out here that's what this just.mock does it's going to say hey anytime this is the way i think about it i don't know if it's 100 accurate but it kind of gets my head around the weirdness of it it's like if you were ever going to import the axios function what i'm going to do is i'm going to give you a pretend version of that so when this gets imported it's actually our little jest mock function and that's why when we log that out that's how it works and it's a way that you can just kind of do a sanity check to make sure that things are working how you expect okay so for now we're not going to need that maybe i'll leave it just in case we do we'll go back to actually fixing this now the the other thing that you might want to know is that this is a package that we've installed so the just.mock you can pretend that it's like an import statement because if you happen to want to mock an actual file or local code that you have you could just do it like this but in our case we just want to mock this package okay so now there's a couple ways to proceed the there i'll show you a different one after i show you this first one i kind of prefer this one in this case but you know it's good to know a few of them so i'll show you a couple but what you can do here is you can import and i like to start it with the name mock and i'm going to call it mock axios from you actually import it from axios it's kind of weird so what this is going to do is it's going to allow you to have access to your mock this here is still going to to mock it for you know the whole run of the test but this gives you access to it so you can do something like mock axios here and then from that point on you can chain onto it and say okay i want the dot get method to mock implementation this is one way to do this i'll show you a shorter one after this and the mock implementation takes a function and that function itself in this case we're just going to do promise dot resolve running out of room so this promise dot resolve what we're saying is what i want you to do is pretend that the get method on our axios that we're mocking returns this that's what it does okay so now it says swappy getter should return a name that's the test notice we're getting undefined and that's a good step in the right direction because what we're doing here is we're just returning promise.resolve with nothing essentially all right so let's actually mock that data now we know if we go back to the function that there is this res.data.name is what we want to get back after we call our get method so let's just pretend that we have that let's just make an object up and return it and say this is data and that's an object and that has name and then from that point on let's just do let's get it to passing first skywalker okay now we get some formatting and you will see that our test pass and where we log the result right here it's actually logging it out correctly so let me show you this it's kind of neat so i'm going to do mock you know jedi or something fun i like to name it a pretend value when i'm testing it out just so that i know that it's not a real one and uh that's also it just came to me another good reason to be mocking something because every single time that you run your test suite you don't probably don't want to be hitting their api um you'd want to do that in an integration test suite which would be probably run a little bit less often at least in my case it is okay so we can see coming back to it that uh the test properly fails when we don't get what we want so let's actually change this to say mock jedi and now it's passing now what's cool about this and this is why i like to do the uh the import you know mock axios from axios thing that we're doing here is because you can actually do something sweet like this where you say uh hey i that mock axios.getmethod i want to expect that to have been called we'll start with this one okay so it looks like it passes right and then let's see times just so you can see it not working there let's do two to make it fail come back up here expected number of calls to received one that's pretty neat huh so we're essentially the mocks themselves they keep a a store of data on them and it one of the things that is keeping track of is how many times has it been called with this test suite here and so within this block uh we're calling it one time now it's preferred and from everything i've searched up and this is how i do it to use two have been called times instead of just two have been called there's nothing wrong with who haven't called it's just cool to know that you know your your function got called but times not only says did the function get called but it also makes sure that it only got called once because what if we accidentally called it multiple times well we definitely don't want that so that's just a good little safety check that you can add on there and importing that mock axios allows you to do that really easily and i like that now because this is such a common thing you can actually shorthand this is really cool mock implementation you can do mock reason oops it's lowercase mock resolved value you can get rid of this whole function here and this promised resolve that we're doing and we can just return let's actually do it here and then we'll get rid of this one this is so hard to see here uh actually we want this there we go so what we're doing here just formatted this to make it a little bit easier to read for a moment we're saying mock axios dot get dot mock resolved value and then we just turn whatever we want so that's just a little bit of a shorthand for that way that we previously had it and it's a little nicer if you do it a bunch instead of having to write a whole promise and return that just so you know now you could end right here and think that you've got this all solved and beautiful but you've actually left a bomb for yourself or someone else maintaining your code base in the future let me show you that so i'm gonna copy this test so we have two tests running and uh you gotta you can't have a duplicate name so i'll just say duplicate test just to show that we're gonna run the test twice and check this out we fail because we received number of calls to so the first test passed but then it says the second test had two calls in it all right and that's because uh we're still keeping track of that state of how many times it gets called it's throughout this whole suite running here and so if we were to say you know set that to 2 that would make it pass but that's not obviously what we want if we want to test it multiple times we want to make sure it only gets called one time each time so there's this cool little method you can go up here and there's an after each and before each that you can call here and it kind of depends on your situation which one you're using most of the time i just stick to one but there are occasions where you might want to do it before every test or after every test and that's what it's going to do so you can give it a callback function here and tell it uh you know things you want to do before each test you can use this to do just about whatever you want you can set up some like local variables or objects or anything like that that you like but what we're going to do here is there is a method that we want to pass that's just dot clear all mocks so in this case now my test will pass i can get rid of this log so needed so i'll clean that up a little bit and now my test will pass even though i'm running the same one because what we're doing is the the mock data we're clearing that after each test runs okay so i'm going to get rid of this one now we don't need that anymore but you should leave this here because some unsuspecting person will come in and they'll add a test and then they'll have a huge headache if you don't have something like this so it's just a good thing to know now earlier i told you that i was going to show you a different way to do this and so let's do that right now the way that you can mock this so you can actually pass a callback here and so what this will be is i'm i'm just returning an object go away so i'm returning an object here and i want to say that whenever you mock this this is what i want you to return for the module so then you in this case you might think that you want a get method but you actually import the default export here and then you call get from that so what we want to do is say okay we then must have a default that's how you do it uh object and then we'll do get and then we're gonna do that same thing but we can just create a just.fn like just to create it on the fly here there might be a different way to do this it's just how i've done it before and then we're going to do mock resolved value just like we did before and then with date data and then we'll do name and then for the name we'll obviously do mock jedi and see if it passes okay so you can see that it says axios default.get is not a function now the reason that this happens this is a really confusing one to get stuck on is actually we are using es module imports and so we need to do underscore underscore two underscores e s module set to true super confusing and that will make it pass i got stuck on this for so long the first time that i learned this so you can actually do named methods without that or if you're just using require syntax you don't need that but in this case because we're using es module imports and exports and we want to uh use the default here we need that now i uh i i go back and forth between uh which way i like you know this is kind of nice to have it all encapsulated here and then you don't even need to import this one if you don't use it for any of uh the other you know have been called methods or anything like that so it's up to you whichever you prefer um this other way is a little bit cleaner to me and so i just kind of lean toward it but it's your preference you do how you like there's also the option of globally mocking this which i won't get into in this video that's another way to do it you know if you're using axios all over and you wanted to mock it so that always returns the same thing that's also an option so you can check that out in the docs but for here i think that we have uh achieved the purpose of this video and hopefully you have a bit a little bit better idea about mocking and how to mock uh asynchronous functions in particular uh the axios package in this case and so wrapping up i'll leave a blog post link to the one that i mentioned previously the accompanying blog post for this if you want to just review it quickly or you ever need to check up it's a little bit older so the material isn't exactly the same but it's pretty dang close and it's nice to just have a quick little syntax review every now and then just in case you do so thanks for watching the video and i hope to see you in the next one you
Info
Channel: Swashbuckling with Code
Views: 7,983
Rating: 4.9693484 out of 5
Keywords:
Id: gA-uNj2FgdM
Channel Id: undefined
Length: 21min 50sec (1310 seconds)
Published: Wed Feb 10 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.