JavaScript Testing Introduction Tutorial - Unit Tests, Integration Tests & e2e Tests

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everyone welcome to this video in this video I'm gonna dive into one topic that a lot of people are struggling with or are afraid of and that is testing your code in this video specifically testing JavaScript code testing can be or should be an important part of any major project you're working on and still a lot of people don't do it because it looks too complex it's too unclear how it works and so on in this video I'll take a look at what exactly testing is why we would want to test and then of course most importantly how to test our code and you will learn that there is no reason to be afraid it's actually pretty easy to get started with testing so let's dive in now what is testing and why would we want to test now testing when you hear it for the first time sounds pretty obvious pretty straightforward we are writing an app we're creating an application and we simply test it right we had a feature we open the browser and we test our application and that is indeed how you will continue to work even if you do add this kind of testing I'll dive in in this video so testing manually still is something that makes sense you want to see your application run so in the end the idea always is that we have our code and we have some expected result so if we write some code and then we go into the browser and test this code we have some idea of what should happen we want to open that modal we want to add that user when we click that button something like this so we have our expected results in mind and then we simply test our application and if we are successful we are fine with that we can continue with writing the next piece of code and if we're not happy with the result well then we can modify our code and fix the issue or adjusted such that it works in the way we want now the idea of JavaScript testing or code testing when you read it like this is that we automate this testing part so that we automate it so that we don't have to manually test everything in our application now we will probably still test everything manually but automated tests can be run whenever we change something in code to see if that affects any part of our application without us having to test everything manually again that is the idea behind automated testing that we can see breaking changes were issues in our code may be introduced by a change in place a in a totally different place of our app that we can see this instantly that is one of the major benefits of writing tests so that is also one answer to the question why would we want to test we want to get an error and CDR if we break our code we want to see that immediately without us testing everything manually we might still do manual testing but it's still worth a lot to have a suit of predefined automated tests that we can run to quickly see did this change break anything well with well-written tests we'll get the answer right away we'll also save time if we don't have to test everything manually over and over again and we are all forced to think about possible issues or bugs when writing our tests we have to think about what do we want to test how do we write a test that makes sense and I'll dive into that in this video already we also can integrate it into our build workflow this is something more advanced but there I mean you could have a build workflow where you push a commit so a get commit to your code repository like github and then you have a workflow that automatically is triggered where this code is then tested in the cloud on some server and if the test succeeds it's maybe deployed automatically so you can build a very complex deployment chain there and tests are then an integral part of ensuring that no breaking or invalid code is deployed so that is also an advantage of tests or something you can use tests or we can also break up complex dependencies and that is also something I'll show in this video we have to make sure we write code that can be split up that is modular because this will make testing easier and ultimately it will make working with our code easier and it improves our code therefore since we are forced to follow certain patterns or to write code in a certain way or at least that will make testing easier we will write better code all together so these are a lot of great advantages but they might still be a bit abstract when you have not written any tests so let's dive into testing soon but not before I try to answer one other question and that is which types of tests do we have I'm talking about automated tests so some code we write that tests our code so code that tests code that is a good description of what testing is or what automated testing is I guess and there we have different kinds of tests we have some tests that test a fully isolated piece of code let's say a function that takes some input and returns some output we can test such a function with a test because it has a clearly defined structure and we can say for input X&Y we expect to get output set and that would be a so-called unit test because we test a single isolated unit of our application we also have some tests that test units with some dependencies so we have no isolated piece of code anymore but we might have a function that calls another function let's say and therefore the function we're testing depends on the result of an average function and that is called an integration test because there we're testing more than just a single unit we test the integration of a feature into another feature so to say we also have the full flow which we can test or the user interface so the full application or a part of the full application and this is a so-called end-to-end or also a user interface test the idea here is really that we do what we could do manually as well in the browser but that we write kind of an automated script that executes a certain series of steps for us and then we can check if the result is the result we expected to get now these kinds of tests have a different complexity unit tests are relatively easy to write because you have a small unit and you know it's easy to infer what you want to get as a result the more dependencies you add the more complex it becomes because it gets harder to differentiate between what exactly is causing an error is it a dependency or the function that uses the dependencies also you might have some dependencies that reach out to a server and fetch data that is also a complexity you have to handle and it will show you how to handle this in a separate video and the full user interface test is the most complex one because there you have to define every step that should be executed and cleverly think about what you want to test and what you want to expect there now all the due to this complexity we write tests in a different frequency we typically have a lot of unit tests because if you test every unit of your application and it works correctly you can kind of infer that the overall application will also work correctly so we will have a lot of unit tests we then have some integration tests to rule out that two individually working units don't work anymore if you combine them and we have a few UI or end-to-end tests to test some steps or some flows in our application in the browser in an automated way so these are the kinds of tests and in this video I will show you all three already and with that I'd say it's enough of the theory let's start writing some tests for that I prepared a little demo project to which you find a link below the video of course and you can simply download that run npm install in the extracted folder to set up all the dependencies I have here I don't have many dependencies I only use webpack and then we have a very simple application here in app J's a JavaScript application front-end JavaScript where I import some bad stuff and then have some code to basically set up an event listener to a button and add a user to a list of users when we click that button I got u tilde s which holds D the functions and importing into AB KS I'm using node.js module syntax here simply because that's a bit easier to test and doesn't require a complex build workflow setup on which I simply didn't want to focus in this video but I use webpack to then bundle this all up and get a main J's file which is the file we then use now what you can do is you can simply double-click index.html I got no dev server and place here or anything like that simply to not focus too much on the front and workflow and if you did double click on that file this is what you'll get you can enter some data and add a user and that is all we can do here so it's a relatively simple app but that is great for starting with testing because we can easily confirm if our tests make sense if they do test something we would test manually as well so this is the application and now let's write some tests now for writing tests we need some external tools we need some tools that help us with creating these tests what do we need we typically need three kinds of tools we need a test runner which is simply a tool that executes our tests and summer the results and gives us some output on the results we can use something like maca there that is a popular test runner we also have assertion libraries and there we define our tests that logics are expected outcomes so these assertion libraries give us tools to define expectations to define comparisons conditions we want to check as part of our tests they are chai is a common or a popular assertion library though what we will use is a very popular library for both running tests and asserting and that is just just as relatively new or at least it was rewritten and not that long ago and it's a very popular library for writing tests because of this Bopha test Runner and an assertion library and it's really powerful and a lot of fun to work with for end to end we also sometimes need a headless browser so basically a browser where we don't have to click manually but where we can use the browser API the Dom and so on without a user interface necessarily because we'll analyze it in code anyways and there we can use puppet here which is a very popular solution for that we need to add headless browser mostly for ant to end testing whereas the test Runner and the search library are also needed for that but also then are the only things we need for a unit and integration tests now let's start with jest and with unit tests and you can simply google for a jest to find just j s dot io t official webpage of chess chairs and there i can only recommend trying that out getting started diving into the official Docs to learn way more but of course I'll also walk you for all the basics in my videos here so we'll start installing just now and for that let's switch over to our project and in there let's run npm install - - save dev just because just will be a development dependency of our project and it will be the tool we use for running our tests and for checking our outcomes or for writing some conditions some assertions as its called so this will now install and with it installed we can right our first test and it will write my test for functions defined in Utah je s and their this first function makes up for a great first unit test it has no other dependencies because it does not call any other function it does not reach out to the web or anything like that it simply takes input two arguments and returns an output and this would be a great example for a simple unit test we can write now to write a test for this function you create a separate file into which you'll store your tests and you typically named it file like to file your testing Guto and then dot spec or dot test is and the name is up to you jest will automatically detect files with dot test or dots back in them and we'll run them automatically once you run just which we'll do in a second so now I have my util test dot J's file and I want to test my generate text function for that I first of only to import it here I will use destructuring syntax here to require something from Yuto so from that you tell file where the core code is stored and then the structuring syntax simply means I pull out some items from the object which isn't the end exported by the util file and it will pull out the generate text function so essentially I import the generate text function into my util test J's file and by the way this is the native way of importing ingest tests you could think that you can also use like es6 imports if you had respective exports in your file of course something like this but this will not work not only because I'm not using es6 Explorer exports here that is also a problem but even if I would use them this is not a syntax natively supported by jest this note is like import syntax is supported by jest and therefore I also wrote my code to use nodejs exports because that made it easier to implement just obviously you can also make it work with es modules like you use it and react or angular apps but there you need a more complex build workflow you need some additional packages and I did not want to spend a lot of time on the setup I want to spend time on testing therefore I use this syntax so now I'm using gel or I'm importing generate text in this file now we can write our first test and for that we can simply write tests here this is a function which is now globally available or which will be globally available when we run our test with just so there's a function provided by just in the end and this function allows us to define a single test now we can give that test a name that's the first argument we're not really a name more of a description you could say something like should output name and age typically you kind of define what you want to test in this name or in this description so which output are you testing for you try to put that in your description ultimately you can put whichever text you want in here but it should be kind of descriptive the second function argument is then an anonymous function which you pass in with just a function jest we'll execute to rerun your test so in this function you add your testing code your code that will be executed and now here we can generate some text by calling generate text and storing that in a text constant and there will pass in max and 29 let's say now that is also what I tested here manually entering max in 29 added a new item with text max and 29 years old now I'm effectively testing that part where this text is generated so not where the whole item is generated but only this text max 29 years old is what I expect as an output of generate text because if we have a look at generate text well then we return a text here where we have to name and then in brackets 29 or whatever the age is 29 years old now if we have that expectation we should formulate it here in the test and we do this with the expect function which is also provided by jest now if you would use a different setup with let's say mocha and chai then test would be a function defined by your test Runner whereas expect would be a function provided by your assertion library and Moe search libraries work with the expect function because tests are pretty descriptive in terms of function names we write a test and then we expect something now here we expect that text so we pass the thing we want to look into you we want to compare we expect text is equal to a specific text we do this with a bunch of helper functions that we can now chain on our expectable object here and there you see we have a lot of functions where we could say we expect text to be not a number for example or to be null or just to be and then here as an argument to the to be function we pass the value we expect text to be that could be a number but that could be also max twenty nine years old which is exactly the output we do expect to get because of this day output we can also see here and now with that we have our first tests to find now to run this test with the jest package I will tweet my package JSON file there we have just installed and we have a script up here the test script which is kind of invalid right now here I will just run just and this will automatically search for files with dot test or dots back in the name and execute them or which end with dot test rjs or dot spec j/s I should say so now with that added we can simply run npm test down there in a normal terminal navigated into the project folder and just will automatically execute all our testing files and here we get a beautiful green output it found one test which passed and there we see the one test which passed successfully where it should output name and age and that succeeded now this is great because now if we ever change something here let's say we accidentally output the age here too we changed something in that function and the original function and we break it if we save this and then we run our tests again we now actually get a failed test and that is exactly where tests can help a lot we get a failed test we see which test failed should output name and age and just even gives us some information on what failed there we see that we expect that max 29 years old but we got 29 29 years old and this now helps us debug our code and fix our code of course this is kind of a constructed example here but you could have a typo like this you could be misusing some variable or some argument and now if we fix that we can indeed run this again and get that test passed by the way you can also watch with chests you can add - - watch here and now if you run npm test just will actually keep on watching and you can control the watcher with the shortcuts that are shown there for example force a run of all your tests again or whatever you want to do and it also means that now if I do change my file here and I save just automatically reruns my tests so this is great having this run all the time and therefore ensuring that we get errors right as we change something so that is another great option and that is our first unit test here now this is a first simple unit test now before we move on to integration tests just a quick example for a different kind of test that could also make sense we should also make sure that it does not just return the valid response here because we can get this with this trick as well if I copy the output there I can also just use return and then this text here and if I do that then my first test will always succeed if I quickly comment out that second snippet so my test will not always succeed but I have an error in my function and we have a second test we can add a second test where we confirm that this is not the case so let's say we say should output data less text or whatever you want to name it and there I could also generate a text with generate text but I pass an empty string and I pass null as a number here and then I can't expect that text should be well what should it be like it should be basically just a blank and then nothing years old right well let's give this a try if I save Det it fails because I always get back max 29 years old because I broke that function I had a false positive for the first test because I returned the value which I expected there but the value didn't take the inputs into account now I can fix this of course and return to the function for him I had before and now the second text will fail fail because there whoops I should expect null years old of course I passed an all not nothing and now there I passed you and I have confirmation that I don't have a false positive here so we're adding a second test like this might make sense as well to kind of check for the opposite or check the same thing with different arguments though you could do the same in one of the same tests you can have multiple expects here too so you could generate an ax 28 here as well and then simply add another expectation here text 2 to be an ax 28 years and now this should all do pass but having such double checks that is the main thing can make a lot of sense to avoid false positives and here I have a double check as well as testing the opposite so what happens if I call that function correctly what happens if has nothing for example well then we would expect undefined undefined here right and that would be a test that could also make sense so I'll delete that for now I'll leave it at this simple unit test but you should be aware of the fact that you can write multiple unit tests for one and the same thing then you can check more than one thing in a unit test and that you might also want to check for false positives or for opposites but now let's move on to integration tests so let's now write an integration test and for integration testing if we have a look at all our code which function could we use there well we got the app.js file we got add user which uses a lot of other functions which has a lot of dependencies so testing this function would be an integration test but this is a function that that doesn't either take an input nor return an output it's that it's a function that really added something that dawn so this is something which you might want to test in an end-to-end testing scenario or in a user interface test but an integration test would be kind of hard here we would have to do a lot of manual Dom interaction inside of our test which you can do but which you might not want to do instead there is something else I can test and that is basically a part of that function you could say in the end what I'm doing in the add user is I do select a couple of elements then I do validate the input and then I generate a text based on the input now we could outsource this init into a separate function and that is the part of writing modular code that I mentioned earlier your forstride modular code which also makes your code easier to manage and reuse though so in you told Jas I can export a new function and let's name it check and generate or whatever you want to do now check and generate will get a name and a age as an input and then I will go to my app J's file and I will grab that validation code and move it into check and generate to validate input for and now that's important for the name here and then there for the age like this and then if we make it past this if block I will return a call to generate text where a pass name and age because that is what I then returned here I returned to checked text so in app days where I'm using this output text is in the end just a result of my call to check and generate I don't need validate input here anymore I don't need generate text anymore I'll just import check and generate this new functional import data and app chess and down there out for text is the result of check and generate I can move that up here and here I can now simply check if not output text so if we did return let's say false here we return false and validation fails so if this is returned and therefore if this is not true ish if it is false for example then it will return an add user to and it will not continue creating that element so now it is this the tweak to add user function and this gives us the check and generate function which is great to use in an integration test so let's write the test for it and I want to test if my validity works correctly and I then get the expected text back so it should generate a valid text output or something like this and then here in this function I will test my check and generate function which I therefore have to import and I can simply create my text with check and generate and I've has max and 29 here and I would expect this text to be equal to max 29 years old so my expectation actually is the same as in this unit test but it's not a simple unit test because the function we're testing has more to it we could fail because validation fails or because generate text fails however generate text is kind of ruled out because we have a separate unit test for this and this is also how you want to write your tests by the way you want to ensure that you drill down like to the smallest possible level and write unit tests for all your units so write a test for generate text would also be worth right one for validate input and then your integration tests really just rely on the unit's being tested and ensure that the units also work together well so desert stand the idea behind an integration test so here I have this test and if I now save this it actually fails now I get an error that Valley that input is not defined here and that is true because I do export validate input I don't need to do that anymore I should instead created or define it as a constant the same is true for generate text so that I can use these functions directly in util J's file and then at the bottom I should also export generate text and assign the generate text constant here and all the export validate input maybe and assign that constant so that I could use it in our files too and now you see my tests passed both tests passed now the idea about the integration test is that if I do change something in my check and generate function for example here I use my validate input function incorrectly so the result of that is used incorrectly now it fails and now this is a great example of why I have an integration test I could have a separate test on validate input and validate input does work correctly I can tell you that but my error is directly here in check and generate I'm using the result of validate input incorrectly by not checking for it being false but for it being true and this means if my name is valid then I actually returned false which is of course the wrong logic in check and generate so even though my units work correctly the combination of the unit's fails because I have an error here and this again shows you the power of tests and why integration tests make sense too and unit tests alone could not be enough because all units could be working if you're combining them incorrectly you still have a problem so now it's working again though now that we had a look at unit tests what they are why we use them and integration tests let's have a look at end-to-end tests or user interface tests for this only to install another tool so I'll quit my chest process here and I will install with safe death puppeteer which is a headless version of chrome of the Chrome browser so it basically is a browser we can use to interact with the dom and so on you can even run it in a version with the head so where we see the browser and we can basically define steps that should be executed in that browser so that we can automate certain processes on our web page and then of course tests a result of these processes as well now this download will take a while because it in the end it downloads the entire Chrome codebase because well it needs that to be able to simulate that browser and once it's done I'll be back so I'm done installing it let's start using puppeteer now and for that I'll go to my utility JS file and I will import puppet here so import puppet here by requiring it from puppeteer like this and I will write a new test now and there I will just write should click around for now we'll fix this later or change this later and in this function which should be executed here I want to use puppet here now how can I use it first of all I set up a browser by using puppet here launch now this will launch a browser with some options which I can define here for example I can set headless 2 to false to actually run a browser with a user interface as well you can set a bunch of stuff here and you can find it all in the official docks of puppet here of course I will also set slo-mo here that will basically slow down the automated operations so that we have a chance of seeing what's happening and I'll set it to a value of AD and we can also set some arcs here which is an array where I can pass - - and then for example windows size to launch this browser with these arguments and windows size allows me to set a width height pair so I can set this to 1920 and 1080 for example like this should launch a browser with this size and that of course can be great to also test if certain responsive features are working the way you want so now this will create a browser now actually this will return a promise so we can use async/await here if you want you by placing async in front of this pair of arguments here or this empty argument list and now we can await this to launch the alternative would be to just use then and catch a single weight and the end dust is for you so here I'm awaiting this and storing the result and browser you could say as a next step I can now create a page object by a waiting the browser to create a new page like this now I also need to tell the browser which page to load and I'll grab my URL here and then we can say page go to and then you enter your URL which should be loaded and you also should have wait this because all these browsers related things are of our promises that simply take some time to execute so you have to await each step so now the should open a browser and load this page let's try this by running NPM test and it should indeed open up a brand new window in chromium which is basically like the chrome word the core functionality of chrome and you'll see that this is being tested controlled by automated test software so this was opened automatically now let's go back and let's add some logic here we don't just want to go to that page let's say we also want to enter something for that I want to enter something into my input with the ID name and age so in my test I will await a step where I use that page where I navigated to my app where I will then click into the field with that selector so click takes the selector off the item it should click on once I click there we'll type and again you can find all commands in the official puppeteer Docs so you can really like instruct browser the headless browser what to do so I want to type and I want to type into the same input and we wouldn't have to click on at first but I want to simulate all the steps a user would do I don't want to insert let's say and I here to mix it up not always max so now I type in there I will repeat these steps once I type the name for the H so for the H field I will enter 20 a let's say if you save that it will open up a new window where you can see that it enters these things now we can now also add another step and click on this button at the bottom of our page so in fjs in the end it's this button we're listening on for an event here with ID button add user can grab that selector and make sure we click on that in our setup and now we get a full flow setup where you see that this gets entered and then we click this button here at the end you also saw that it didn't add an item and this already shows us that writing this text was not the worst idea because it looks like something was broken and indeed here in our automated test if we open the developer tools we see it that it didn't find the validate input function here so since that was missing or is missing we have to check our util J's file and see what's wrong with validate input in here I'm using validate input here and I'm defining it as a function here but one thing I never did is I never recompiled my code that's something we have to do because the code that gets imported in the browser is packed with webpack and I never recompiled it after changing my code so in a separate terminal window I'll quickly run NPM start to get web pack to run my code it'll now also watch my code and recompile if I change something and with that it should have executed your test automatically in case it didn't simply add a blank and reopen your browser and enter and it will reopen the browser and execute this and now you see this works and this is puppet here being used for automated testing with a graphical user interface now obviously we can automate this a bit more and check the result programmatically now to see if we got the expected outcome because what I can now do is I can also wait and now that everything executed I can check if something is the case for example I can check for the existence of this list item with a class of user item being added to it so in my test I can await my page and there I can use dollar email to get access to an element and I will look for the first element with a class of user item and I pass a function as a second argument to evil where I then define what I want to do with the element I select it there and here I will get an element as an input and for example return the text content and this means that now the result of this function will be returned by this overall promise so I can store it here final text for example will be the text that I fetched from this element that was created and now I can expect final text QB Ana 28 years old now it's again checking for that text but it's now using the full flow in the browser it's not just the unit or integration test as we have it up there it's really going through all the steps we would execute manually as well and then of course we should have that text but we could check anything here we could also check for the existence of this class and we implicitly do so because if that class would not be there we couldn't select the element by it and therefore final text would certainly not be that text but now if I save this it executes all our steps here and it actually fails here because it basically timed out that's just a limit setup by just to ensure that our tests don't take too long what we can for do for that is the test function actually takes a third argument and that is the maximum timeout and we can set this to 10 seconds to 10,000 to increase the time we give our browser to complete its operation so that it doesn't timeout and now you see it finishes successfully because should click around to which we should assign a better name does give us the result we want and here I will now say should create an element with text and correct class something like this by the way we can't of course our also set headless to true disabled the slow motion and not set these arguments and this will now speed up the testing because now it doesn't have to go through that step in a real browser it does so behind the scenes and that's one of the advantages of having a headless browser that you don't have to watch it execute the steps but it can be nice to see the steps you can do both and now we got a full to a test or a user interface test in place - I hope that this video gave you a nice introduction to the different types of tests we have and why we write them and I will have more videos on testing where we also write a little bit more advanced tests but this video hopefully shows you what tests are and how will you create them and what their advantages are hopefully see you in future videos bye
Info
Channel: Academind
Views: 259,704
Rating: 4.9744339 out of 5
Keywords: javascript, testing, unit tests, integration tests, e2e tests, ui tests, unit testing, javascript testing, js testing, js tests, js unit tests, js integration tests
Id: r9HdJ8P6GQI
Channel Id: undefined
Length: 39min 45sec (2385 seconds)
Published: Wed Oct 03 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.