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