JavaScript Testing - Mocking Async Code

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi welcome to this video I already got a video where I dive into JavaScript testing and I explain what it is why we would do it and how it generally works with the jest j/s package in this video I'll build upon that last video so definitely check that out and the article which belongs to the other video and in this video I'm gonna dive into how we can test asynchronous code specifically how we can test asynchronous dependencies like for example functions we got where we let's say execute some code where we reach out to an API how can we test such code efficiently with just Jas let's take a closer look I again prepared a simple project for this video now you find it in a link below the video in the video description and if you start the project by double clicking the index.html file you'll see a button there if you click that button you will eventually see an all uppercase text being printed here now responsible for that output is this code here I got a very simple setup here using webpack and here I'm getting access to a button in my Dom I'm adding a listener to print a title in print title I call a load title which is this function here in that function I reach out to fetch data which is provided in a different file also created by me there I seem to get some data object of which I extract the title I then transform this title to be all uppercase and I return it hence I can print it here now if we have a look in this HTTP file into this HTTP JS file here I'm using a third-party package Axios so this is not written by me this is an our third-party package and here in fetch data I make a get request to a dummy API so this is a real API in the web a real request is made here this API is not maintained by me it's just an API we can use for playing around for testing and there I get back a response object Axios gives me such a response object which happens to have a couple of properties like the status code for example but also the data property which I'm extracting here this is essentially what I do now let's write some tests I already installed the just package here and I also already configured my test command here to use gest I also have a project where I use node.js import-export syntax because just uses that by default and just as with my other testing video where I gave you an introduction to testing with just there I explained that just use the stats and tags that's setting it up to understand a different syntek would be more complex and I still don't want to do that here because here I want to focus on testing async code so we got everything set up to write some tests let's do that you learned in that last video that you can create a new file with dot test dot J s at the end to have just J's pick it up and executed automatically so this will be the testing fall for app jeaious and let's say we want to test print title conveniently I'm already exporting it here at the bottom so what can we do well in app test j s we can use the test function which is globally available when we run that file with just there we give it a description some text yeah some description of what should be tested here and there I have should print an upper case text for example I have my arrow function then that should execute or that contains the testing logic and in there we define our expectations now we could expect uppercase text with the help of some regex magic or anything like that but here because we'll change that anyways I just want to expect this text because right now I will always get that text for the API endpoint I'm hitting so I expect and now I need to import that function which I want to test so that will be print title I'm pulling that out of my exported object in the app.js file so I expect print title the result of that function call to be equal to my all uppercase text here this is my expectation we can now run that test by running NPM test and this executes all test dot J's files and this actually fails because it fails to execute addeventlistener off now now the reason for that actually is that app J s contains some code that will always run when this file gets executed and it does get executed when I import from that file and it tries to access the Dom here which just doesn't work in this environment here the real DOM is not loaded here so an easy way around that is to take that function and export it into a separate file maybe a util dot J's fall this is also something we did in the last video already export and title here and what this allows us to do is it allows us to import something from that file only without executing a purchase and therefore without hitting the real DOM so we can rename that testing file here to util test jeaious and we import print title from dot slash util and an abscess since I removed print title from there we also need to import print title by requiring it from the dot slash util file here and now for that to work I'll also have to move load title over to you tell Jess because print title depends on that so let's move load title into that file as well that means that fetch data also needs to move in there so from aptus let's grab the fetch data import and move that to the util J's file and now with that setup if we rerun NPM test here now it should be able to run our test here and it does but I get an error that essentially it expected a string but it received undefined and now this already shows us a problem we have with asynchronous code testing well actually in print title I am indeed returning nothing so getting undefined as a result makes perfect sense but even if I would return something like for example in here if I not only lock the title but I also return the title in here if I do that and I rerun I won't still get that error I can already say that the reason for that is that and that has nothing to do with testing that sin Robel javascript behavior return statements inside of callbacks or inside of promise then calls are not executed or are executed but JavaScript does not wait for them it does not return this value as a return value for this overall function instead this is the return value of that inner function here and we're not testing this inner function we're testing that overall function so in our case here one simple solution would be to test load title here we return something in the inner function and we also return something in the main function we return the whole promise so chances are that we get back a promise here too which we then can listen and that might allow us to expect something helpful so let's change something let's export load title here so that we can test it let's go to you tilt SJS and there I will not expect something like that but I'll get my title but instead I will import load title here and it will execute load title and this gives us a promise so I will add then here and in there I know I'll get my title because that's exactly the way I use it in print title right I have loads title and then I get my title so now I have the exact same usage here in you tell test j/s and now here I want to expect something that my title should be equal to that whoops to that upper case tile I got here so we can grab that title here copy it and that is what I expect as a value now if I rerun NPM test it actually passes because now this test succeeds so this is better this is how we could test our asynchronous code but we still have a problem here the problem is we're still hitting our API so here in Utah yes I'm using fetch data and if you remember we're defining that in the HTTP file so here and they were making a real API request we typically don't want to do that in testing we might exceed certain API quotas if we do that if we're testing post requests we might even edit something on the API which we certainly don't want to do as part of testing certainly not with our production API at least so hitting the API is typically not something we want to do there also are other reasons against it what would we achieve by really making that HTTP request do you want to test if your API works correctly if you're building that API you should do these tests during the API development not when working on your front-end these two should be separated so that is certainly not something you want to test if the API works or not is not something you test in your front-end code you might test error fall backs or anything like that but not if the API works correctly you also don't want to test if the get method exposed by Axios works correctly that is a method provided by a third party package and we rely on that package doing its job so we also don't test third party package functionalities we want to test our own code and our own code here for example is that we extract data and that here that we transform this to application that we pull out the title now to focus on that we do something which is called mocking we mock features which means we replace features we don't want to test with some dummy implementations and how it could this look here well in util test Jas we got load title and we want to test if that successfully transforms our title to be all uppercase to be this text now to test this what we need to do is we need to replace fetch data with a mock now just gives us a great way of preventing us from using that fetch data method we can create a new folder called underscore underscore mocks all lowercase underscore underscore next you the files that contain our raw source code so in this case in our root folder now here we can't add HTTP dot J's false so the same file name as the file that has the function we want to replace and now in HTTP J's we can now do some magic to set up some functions that will replace our real functions when running the tests for that I'll copy my code from HTTP I'll get rid of axis though because I'll not use that here I'll still export fetch data though but in fetch data I'll simply return a promise which I instantly resolved with promise resolve to an object that has a title property with the text I want to test which I'll now convert to all lowercase though so the lectures aunt autumn and now that I'm exporting this here something interesting will happen in HTTP j/s I added a console log statement to fetch data to see when we're hitting this API and when we're not if I now run NPM test we are hitting the API as you can see here we have that console log statement from the HTTP file in there now we can go to util test j/s and in there we want to add just mock at the beginning and mark the HTTP J's file like this you can omit touches though now if we run NPM tests you see it also took close to a second because it had to do some magic bandit scenes but you don't see that console log statement because what now happens is when this test gets executed when this testing file gets executed just goes ahead and replaces the HTTP file with our mocked file so all the functions that are exported here are then replacing the functions we are normally exporting in the real HTTP file for our tests only so for the tests only we use our mocked functions here so this dummy fetch data which gives us back an object that allows the other functions to work fine but which does not hit the API because we don't want to test the API response and we don't want to test the access get method so we can rely on the API returning as an object that has a title property and we can rely on the axis get method giving us an object and a response which has a data property so we don't need to test that we want to test whether our code in the util JS file correctly extracts the title and transforms it and that is the case otherwise this test would not have succeeded for our marked implementation of fetch data where we return an object with a title that is all lowercase so not what we test for lowercase this text and therefore since our test succeeds we know that our transformation here does work so this is how we can test async code or how we can replace code how we can replace functionalities if we don't wanna in this case hit the API the same would be the case for let's say code that accesses the filesystem you don't want to do that instead you want to mark such functionalities you can by the way all the Mauck global packages like axis itself instead of marking our fetch data function you could create access dot JS file here and there we could export our own get function here which we of course have to define because remember in my HTTP file I am using the get method of the axis object so here I want to export a get method and that should be a function that takes a URL as an argument but we don't care about that URL here what we want to do here is we can return an object which in this case since this is faking the response from Axios so we want to fake whatever get gives us back and that should be a response object which at least has a data key because we're extracting that so here in the axis mark we want to have a data object which shouldn't turn be an object with the title we want to test so that lowercase title we define here so basically we're now marking a functionality which is one level above the last functionality where we mocked our own function now we're using we're mocking the function this raw function so this function depends on so now in the axis J's mark file I'm returning an object which has a data key which has a object as a value which has a title key which is the lowercase version of our text and with that being marked we can go to our util test J's fault we can comment out this marking of our HTTP implementation so we'll now not use this HTTP file we've created a second ago gestures will automatically use mocks of global node modules though so x SJS this mark should be used automatically we don't have to instruct just to do that and now we can simply run npm test and here it fails because what I returned here is not a promise and this already proves that this mocked fowl is being used otherwise it would not have failed I'm returning an object there and therefore calling then fails I of course have to return a promise here by using promise resolve so a promise that eventually yields this object and once I do that and therefore my macht get method provides a promise as the real axis implementation does once I do that it passes again we see fetching data here because we are now using my original fetch data implementation but the get method we're using here is not the real one it's our marked one so this one here and this is how marking works we've just now below the video you find some article or some links to the official Docs with more articles more information on how mocking can be done with jest but I hope that this video also was helpful with understanding why we mark and how we can mock so how we can replace certain implementations which we don't want to test in our code that depends on them so I hope this was helpful hopefully see you in future videos - bye
Info
Channel: Academind
Views: 143,754
Rating: undefined out of 5
Keywords: javascript testing, js testing, jest, jestjs, mocking, async code, testing async code, jest mocking, javascript testing mocking, tests, unit tests, integration tests, e2e tests
Id: 4Fl5GH4eYZ8
Channel Id: undefined
Length: 18min 4sec (1084 seconds)
Published: Wed Oct 17 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.