Test Driven Development - TDD with Node js Express

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome to this course about test driven development with node.js my name is bashar burkharman in this course we will be building a fully functional backend service with express.js we will call our application as hoxify while building this application we will learn how we can build a restful web service with express.js with all necessary functionalities like validation internationalization static resource serving caching json manipulation and interacting with external services and we will see how we can handle database operations with orm package of sqlize and we will see how we can manage database version history and migrations and while doing that we will see how test driven development works and how it is affecting our code quality and reusability and how it is giving us the confidence about refactoring our implementation also we will deploy our application to heroku and we will create an instance in google cloud and deploy our application to it and also we will automate all this deployment process with github actions this course is purely built on practice you will be learning by doing it each code piece we write will be for our actual application implementation in each section we will gradually build our application we will not jump ahead and add a functionality not needed for that moment we will implement one requirement at a time each implementation will bring the next requirement to us and following this practice will help you to get a solid foundation about overall rest web services requirements and how to implement one of them with node.js by following test driven development methodology now let's see how hawksify is working we are going to build a backend application but to get a better understanding about all the requirements from user point of view you will find a web client shared with this course now let's demonstrate the functionalities we declined in this application we will have users and users will be posting hoaxes and let's go over the functionalities one by one so we have this sign up page and in the sign up we will be filling the form with the username email and the password and if user already have an account it is going to be redirected to this login page and if user forgets the password we will be providing password reset functionality now let's start with the signup so when we try to sign up with the existing user let's say user1 we have this user and it's email address user1 mailcom and the password password let's send the signup and we will be receiving this email in news validation by the way our application both the client and the backend is supporting the internationalization so if we switch the language and retry the request our backend will be responding message according to the selected language so let's sign up with a non-existing user let's say our user is user 100 and the user 100 mail is this one and the password is this one now let's split the screen to show this web client and our application console so here on the right hand side our application is running now sending a sign up request and we get this message an account activation email is sent to this user 100 and we have this url printed out in the console logs of the application let's copy this one and open this url and here we have an email from our application for this user 100 and here it says please click below link to activate your account and let's click this and here we are going back to our web application and it is saying that your account is activated now and we can log in with this user but let's say if we forget our password then we will be posting our user 100 at mailcom again it is saying that check your email for resetting password and we have another url right here let's copy this one and opening the page and this is for the password reset and clicking this reset and we are getting this page for resetting our password and we can just fill with new password and your password is updated page will be redirected to login page then we will be able to log in with this user so let's log in with this user user 100 newcom [Music] password now we are logged in and let's close the other tabs we no longer need them and we have this user listing functionality and we are listing the users with page nation so we can go to next page or previous page and we can see the users like this we can go to our user profile and edit our user so let's say we will change our username let's say user 100 and we can also choose file for our user let's select this one and save it and our user is updated now now let's look at this user let's login with another one let's go with user one and when we log in this users list is not containing the logged in user express checking the authentication fields and for this user listing case we are just excluding the logged in user from that list and let's go edit our user once again let's say user one update it and we have this profile picture for this user and let's submit a hoax with this user so hello world and also we can submit hoaxes with attachments so like this one we can add files to our hoaxes so sending this one and if we refresh our page we see the hoax or we can go to our page users page and list the hoaxes for that user and let's log out with this user and log in with user2 and let's update this one too user to update it and let's choose a profile picture like this and let's go to hulk submit hello world 2 and this one is also sending in hoax with an attachment sending it refreshing the page and here we see the hoses are listed and if go if i go to my profile page i see my hoaxes and if i go to user1 i see hoaxes of that user and we have this delete functionality for the hoaxes if we have this hawks we have the option to delete it and for the others we don't have that option also we have the delete functionality here in the account part by the way these this client is showing this conditionally like the delete operation for instance in the hoxxes or in the user profile page for this one we don't have the delete button but for my profile we have this tweet my account but in the backend part we are handling these requests based on the authorization coming in the request so even we are limiting the user actions right here on this client in fact anyone can send the corresponding rest requests to delete this host for instance but our backend is properly implemented to handle such cases like rejecting those requests if user is not authorized to delete something we will be returning corresponding error back to the user so like this one we can just send the delete and the hox is gone and also the attachments are gone from our storage and let's log out with this user and login with this user one once again [Music] and if we go to our my profile and delete my account in this one both the user and that users hoaxes and if the attachments those attachments are also removed from our application so that is how the application is working and we will be building this application with test driven development so let's show the final result stopping the application and i'm going to run the test with coverage mode normally coverage does not mean anything in terms of the quality of your test it is showing how many lines of your actual application code is being run through the tests but again it is not reflecting the quality of your tests so let's run pxjest with this coverage just is our test tool we are going to use just for running the tests and this coverage is an option to also generate the coverage report for us so hitting enter and here is the result we have this much of tests and all of them are passing and if we look at the report our test code coverage is around this one and that's it we are going to be doing our implementation by adding our tests first we will start with writing our test code then we will be implementing the corresponding functionality and you will see the the benefits of test driven development throughout the course and hopefully this course will help you to embrace this methodology for your software development practices we are not just going to be doing the implementation our local environment and leaving it like that we will be deploying our application to heroku and also we will be deploying our application to google cloud vms and also we will be automating whole deployment process with github actions so we are not just going to practice the application development but we will also see the practice of actual production environment requirements for any application so if you are ready let's start building this application for this course we will be installing couple of applications to our workstation and the first one is nodejs we will be developing our application with this javascript runtime environment and it is coming with npm node package manager we will use npm to install packages and run our application scripts even we will be automating our tests to visualize our request response output we will use postman this is a very popular http test tool in our application we will be using sqlite database this will be easing up our development and test environment to visualize the content of our database we will use db browser for sqlite and we will also use postgresql to demonstrate how easily we can point our application to that database and for that we need to install pgadmin to our machine for code editor we will use visual studio code and we will use couple of extensions one of them is this eslint for linting feedbacks and the other one is this printer this is for having a consistent code format in our application and the other one is sqlite again this is for visualizing the sqlite tables but it is not very robust while i was recording this course we will make a practice of deploying our application to heroku and that requires git version control system so even you are not using as your daily software development practice now it is required you to use to deploy your application to heroku and finally we will be connecting to an actual server and for that we need an ssh client and for windows we need a client like putty and here is the download links please install these applications to follow the course we will follow test driven development methodology in this course this means we will write our test code before implementing the requirement test driven development the tdd will be guiding us to clarify what we are implementing and how we are implementing it it has cycle of red green refactor phases in red phase we will be laying out the requirement with the test code we are writing there is no implementation for passing that test at that moment then we implement the code that does the requirement we run the tests and see if our implementation is passing the test or not if we are passing then we complete green phase then for refactor phase we can change our implementation until we satisfy without breaking existing functionality these three steps are letting us to be aware what we are implementing to make sure that our new implementation will not break our previous tests and to write clean and reusable code now let's create our project in our project folder i'm running npm init this is going to be asking us couple of questions and we will be giving responses so it is asking what is the package name let's call it express web service and version 1-0 and let's say tdd with express for the description and it's asking what is our entry point and we will have let's say app js and it is asking our test comment but we will edit later and looking for git repository for now skipping it and skipping the keywords and you can type author name as yourself i will be writing my name here and the license part so it is basically generating a file package.json with format like this so it's asking if this is okay and we hit enter for yes now we have the package.json let's open the project in visual studio code now here is the package.json we created now we are going to install dependencies to build our application main dependency is this express we are going to build our application based on express and we will install it with again running on terminal at the folder where the package.json is we run npm install express [Music] so this will be downloading the dependency for the express and it will be adding it to our package.json so let's confirm it here here we have express here in the dependencies part of this package.json now let's create our initially entry point module let's call it fjs and let's create a simple express application for that first we get the express from the express package we just installed then we create an app [Music] like this and we tell app to listen this is taking two parameters the first one is the port that it's going to be listening so i'm going to set it as 3000 and the second parameter is the callback function when the application is listened is successfully initiated then it's going to be calling this callback so here defining an error function and let's say let's look to our console that app is running so saving this we can run this application again opening the console here running it via node and the javascript module name app so here we see the app is running and if we go let's say that this browser and if you type localhost 3000 here we are receiving this response for now there is nothing in our express application so by default it is returning back this response alternatively we can run this application a script we defined in package.json so let's start this one with ctrl c then here in package.json just like this test1 we will have let's say start and this one is going to be the command we typed in the console node app so we keep both of them here again saving and this time i'm going to run npm run start so here we see our app is running for start script we don't have to use the run part instead of running npm run start we can run npm start so the app is running now we will also a couple more dependencies we are going to do a test driven development and for that we will need a test runner we will use just for that so let's install it running npm install dash dash save dash dev this is a development dependency so installing it like this and telling jest [Music] now let's update our test script here in package.json here we can see the chest is added to this dev dependencies part now we can update our test to running the jest and we want just to be running actively which means whenever we do changes in our code just will be automatically picking up those changes and re-run the tests so we will be continuously receiving feedbacks while we are developing our application to do that we are going to add dash dash watch parameter but this is going to fail let's see this is going to fail npm run test or just like the start case we can run this test script via npm test so running it so it is trying to find the test modules then keep on in watch mode but it is failing because the project we just created is not a git repository right at the moment and watch functionality is trying to figure out the differences between each commit and then decides which test to be run so that's why it's failing alternatively we can use watch all which means it's going to run all tests so it's not going to check the commits git comments and it's just going to find the test modules and run them all we will be keeping track our application with the git so we will be replacing this part with watch so let's do that here anything a git repository here so reverting it back to watch and running the tests one more time so this one is now in a git repository and then just does not complain anything about it and tries to run the test and here we see no tests found related to the files changed since last comment so there's nothing to run we have one more final dependency to install and that is the super test this is for initializing the express application in test environment and running the http requests against that application so we are going to again install this package with npm install again this is going to be a development package so adding dash dash save dev and our package name is super test now we have the the fundamental dependencies added to our project to have standard code style and to have assistance from our editor for catching problems early let's add couple of more dependencies our first dependency is a popular library called as eslint it will be helping us to have consistent coding throughout the project so let's install it again this is going to be a development dependency and the package name is eslint now we are going to add configuration for eslint and here in this eslint page it tells us there are two possibilities for configuration one of them is the estint rc file and the other one is putting the configuration into package.json with the field of eslint config we will do this one so here in package.json adding eslint config our first property for this configuration is to define parser options we define what is the ekma version ekma version is the standard which the javascript is based on so we are going to use ekma version 6. so it will be running linting against this version then we want to use some predefined rules coming with the esd so we tell that we want it to be extending the and for this one we can pass an array and we want this to be extending the eslint recommended rules then we define at which environment we are developing our code and this is going to be node environment and also it's going to be es6 and also we are using the chest as a test runner and it is coming with some global functions so we want eslint to be aware of it so we tell we are also running jest and then we can define rules here and let's say one of the rule would be the semicolon after each line is a must and we want to see a warning if you don't use semicolon so we tell semicolon is a warning if semicolon is not used and we will get the warning and for let's say quotes we want the codes to be single code if it's not then we are going to see a warning so for that one we will set the value in array and we will see a warning and it's going to be in a single so saving this one in visual studio code there is this extension this eslint extension if you have this one then if we go to our fjs here you will see these linting errors here it says this must be in single quotes so if we just change it then the warning for that line will be gone but we will not do that manually instead we are going to use another library for that the prettier we are going to install prettier also we are going to integrate the prettier with eslint so it is describing how to integrate the prettier with it so we will do that now so installing the prettier this time so running npm install again this is a development dependency and we will install print here but in single install command we are not limited to install just one package we can install multiple so let's install the other packages that will be needed for integrating with eslint so we will have eslint config spread here and the next one is eslint plugin red here so all of them are installed again we have an extension for pre-tier for the visual studio code this one provides some functionalities like formatting code automatically for us and we will use that functionality so let's configure eslint to work with the printer we are going to add prettier to this extend part it is plugin print here slash recommended and also we will add plugin and this will be prettier plugin we can also configure pre tier here just like adding eslint config we can add configuration for print here and just like defining rules we see here we can define pretty rules like single quote to be true so if we run pre-tier it's going to replace double quotes with single quote automatically now we don't need to define the rules here because by default adding the semicolons automatically saving this one and opening the fgs now we have two warnings one of them is coming from our eslint the other one is coming from prettier so let's for now pass just an empty object saving it and here we have the warning just coming from the printer now to fix that we can run our extensions capability to do that we hit ctrl shift p to get this command input place then we can find the format document and it's going to replace the double quotes with single quotes i'm just undoing this to show one more thing as you can see this is underlining with red line but before we had the print here in eslint config we told it to be warning so we can override this prettier behavior like this one we can say prettier slash prettier so that is pointing to this part prettier slash spread here to be let's say warning and saving it now we see warning instead of error now let's try something else let's first fix this part warnings are gone and let's make this line a bit longer let's add a couple of more characters and here we see another warning and this is this warning is coming from eslint the coming with the prettier rule it's not liking the length of this line by default it is expecting 80 characters to be in this line but let's change it to something bigger let's say our print width that's the parameter and here you see the default value let's set it to 120 saving it and getting back to fjs and now the warning is gone by the way if you are using windows then you may be seeing a warning like the end of lines are being shown as warning by default pre-tier is looking for let's find the parameter here and offline the default is the lf so it is looking for just slash backslash n the common linux macos line end but if you are running windows it is possible to be crlf you might be seeing that warning to be consistent let's continue to use as line ending the lfs you can change line endings in vs code with ctrl shift p and change end of line sequence and you can switch from left to sierra left also you can set it as a setting here and in the settings section you can find the end of line here the files aol to be the slash backslash and so if you just save it like this you will be automatically having whenever you hit enter it's going to be backslash n and let's add one more rule here for the esl part we are going to use triple equality and this is this parameter so we are going to use triple equal whenever possible so we want us to be warned if we are not using this one so saving it now with the printer our editor is not allowing us to save a file like this one let's do this way let's replace the single quotes with double quotes and we are going to save the file without formatting so we have this warning we can also have a script like start and test let's add one more script here let's call it lint and in this script we can just run eslint in this directory so it will be running from our root directory and it will continue to go deeper to the nested folders so saving it let's run this in console npm run lint so it is scanning the modules then here we see the warnings now we can also fix the this warnings by running as you can see it is saying you can run with the fix option so it's going to be something like this the fix is lint dot fix but when we are trying to pass an argument via this npm script we have to add two dashes then add a space then we pass the option so let's hit this one so here you can see it is running eslint with the fix option now let's run eslint once again now the warnings must be gone here we see warnings are gone and when we open the fjs here we see the double quotes are gone [Music] now let's add one more dependency to our project we are running our tests in watch mode which means whenever anything changes the tests will automatically run we will also do same thing for our application running behavior so we will use a library called as node mode so let's call npm install save dev node mode so this is installed now let's split the screen to to this terminal and the editor so here instead of let's resize this now instead of calling the application with node app let's replace the node part with node mode and let's in console let's run npm start so here we see the application is initialized through this nodemon package now when we come here and change anything let's say app is running and here we see the application is automatically restarted now we have completed the basics of our application dependencies and the packages we will add a couple of more dependencies but to make sense why we are using them we will install when we need them we will be building a web service a backhand application but to understand it more clearly from user point of view i'm sharing a basic client with this course so let's run this client for that we are going to use the npx this should be coming with npm package manager if not then you can install it like pm install dash g and px so if you don't have it please install this way so what we are going to do is we run npx and we will be running http server by the way beware we are at the folder where our index html is okay so we will run http server and we will pass couple of parameters to this one first of all it is by default caching the static files we don't want that so for that we add dash c dash one and let's run it on specific port with the dash p parameter and support i'm going to use 8080 and we want this client request to be proceed to our actual express backend to do that we are going to add another parameter with dash capital p uppercase b and we would be pointing this one to http column forward slash forward slash localhost and the colon port is 3000 this is the port where my app is running so if we hit enter this is going to be installing the http server if it is not already in your machine and then it's going to print out this interfaces that you can access to it so if you open the browser and hit localhost 8080 it's going to be returning back to us this application this index.html we have this login page signup page and also you may be realizing that there is this flags there is translation so you can just add your translation if you like so here in this locales there is english and turkish and in this file supported languages you can just edit it i'm opening here you can add your language you can replace this tr one with your language and here this is a free api that is just providing count reflex so you can just find your one [Music] here there are lots of flags here you can use your country flag and so if you just let's say you want to add german version you set languages d e and flag part with the d also then if you just refresh it you will see the german flag so it's not going to mean anything right now because it's trying to find the translations for the de but let's display it here so it tries to get the translation for this low ksd translations to german version but it doesn't find it because it's not there so it is failing to translate it so what you need to do is to add a folder just like this one let's say copying this one adding de and opening this file and changing here with the corresponding translations so that's how you can add your own translations to this client application so we will be implementing the functionalities step by step and you will see failures for the not implemented ones so let's not care about them so we will have just sign up or we will have this login and we will have the forget password parts so we will do them step by step so this client will be useful to visually make sense about what we are actually building because when you are just focusing to the back end it might not be always clear about what is the actual user flow to understand the user requirements and to see how it's going to be used by a real user then that will lead us to think about what are the requirements on the backend side for that particular service so i will be using sometimes this one sometimes i'm going to use the postman for sending rest requests consider this one just for a test client now let's start implementing the signup functionality so remember we are doing test driven development so we are going to start our implementation with writing our test first so first we are going to create our tests folder double underscore tests and again double underscore just is automatically picking up this folder when it's trying to find out where the test files so we are going to put the test files under this folder now let's run npm test so we have seen this one it's running in watch mode but since there is no test yet it is just printing this logout now let's create our first test module for test module names there is a required naming convention for just to find them it is either endsvit dot spec dot js or ends with test js the spec is for defining the specification of the scenario so that's why i prefer this one against the test so here in this one we are going to test the user register we will define the specification of the user registration so as soon as we add this file just is trying to run it but it is failing because it says there is no test suite in it let's add our test function again we have two options to define a test function one of them is to add test or the other one is it and again i'm going to use this one because with this way we type it then we describe what is the test about for the first test it is going to it is the our application it refers to our application for me so it returns 200 okay when sign up request is valid so this is our test description and for our actual test we will pass a second parameter the test function here so it's going to be an arrow function again and in this one we are going to initialize our application and we will send the rest request to it to initialize the application we need the library [Music] request we will call it as a request and we will require the library we installed supertest so we are going to request and this is looking for the application so we will pass app as a parameter to this one but first we have to go to our app.js and export this from this module first so that we can import in the test module so going back to test module and here getting the app from the the one directory above we are in in the tests folder going above and here it is app module we are requesting app and then we will change couple more functions to this one this is the supertest functionality we are going to send a signup request so we will be sending some data for the restful services when you are sending a data to backend you should be using the post method if you are sending it for the first time so if you are sending something for creation then you should be sending it with post method and we will be sending our request to an endpoint naming of this endpoint should be aligned with our entity our object so we are going to send a user object so our endpoint should be something like this user but rest is suggesting you to use plural namings instead of singular one so we are going to send our request to slash users and also it's good practice to have api in somewhere in the path and also the version of the api so like api one zero users would be fine so we will be posting data to this endpoint and the data will be sent [Music] in a json format so let's create a json object here with valid user request in it so what is a valid user request it will have a username so let's say it's user1 and it will have email let's say user1mail.com and there will be password and let's say password will be something like something uppercase and a number and a lowercase let's say some character long like this one so we have these warnings coming with the printer we can just get rid of them with just calling format documents so it is shaping up better now we are sending a request but we are looking for a result to make sure this test is testing actually so in the end we expect to receive 200 okay again warning because of the format so running format document so the test is ready let's save this test is passing but we didn't do anything about it it shouldn't be passing so we will get back to it but let's focus to this part we have this warning it's it's complaining about this console log uh because the test is already ended but it's trying to print out this slope we are initializing the application when we are importing this module but in our test we don't need it to be running in listening mode so what we should do here is let's add another module let's call it index js now as soon as we add a new file to our project jest is detecting it and running the tests again and we see this failure address already in use and the reason of this failure is when we run our test for the first time we run the app in listening mode and it is using the port 3000 and when just tries to run the tests again where we initialize the app once more and it is failing with this address in user so other than the unnecessary console look we see in our previous test result the main problem coming with the current implementation is it is breaking our test watch functionality so let's move this app listening mode to index.js and in index.js let's get the app from our app module and instead of running this listen here let's run the listen here formatting the document and now saving this one after our changes tests are running automatically again but this time the test is crashing you may not have seen this issue or maybe you have seen this crash in the previous step this instability is triggered with the sport usage now we have extracted the app listen functionality from app.js so we will not face this port issue anymore in our tests let's run the test again so now the warning is gone because our app is not in this module it's not in listening mode anymore we are running the app in index js and let's also update our package.json to start the application not from the app module but the index module so saving this one also killing this test console and see if the application is going to be initialized so running npm start and here we see app is running so index.js is properly initializing the app in listening mode now let's go back to our test again now the warning about the console log is gone with this changes now what we are going to do is we shouldn't be receiving 200 okay for this case the test is passing because our expect is not actually waiting tests to be done this expect is asynchronous so test is finishing without an error before we get this assertion part to tell the test runner just that we are waiting for an asynchronous action to be done we take the done as a parameter for this test function and we will pass it to this expect as second parameter so saving it now we see the failure now instead of receiving 200 okay we got 404 not found so let's fix this we are going to create a route handler here so for this application we will be handling the post request and we will be processing the request going to api 1 0 users and we will have a function to handle this request let's define it here this function takes two parameters one is the request wreck and the other one is the rest response and what we are going to do is return a response return response and we can just send an empty response back this is by default going to be returning 200 okay so let's save this so here we see test is working to make sure that our implementation is doing something let's change the status to something let's say 500 and here we see we are receiving 500 instead of 200 okay so our implementation is working so for the assertion part we have alternatives like coming with jest so let's use that so this is an asynchronous action it is sending a request to backhand and waiting a result so we can wait for the result here by adding so commenting this expect part out by adding this done function as a parameter we are going to define a function and this function will be called with the response object and we expect this is the expect function coming with the gist not part of the super test response status status code 2 b 200 ok and again we call done to tell just we are done so again formatting saving test this one is also passing now let's add one more test it's always good practice to send a response body back to client to be descriptive about what has been done we can do this check in this test but it's not good practice to do multiple assertions in a test so adding a copy of this test copying it and pasting here and this time it it returns success message when sign up request is valid so this time we expect the response to have a body and this body will have a message property and it will be let's say user created [Music] so this is failing because we are not sending a response body back so what we are going to do here is just adding a body object to this sent function so we will have a message property and we will have user created for it now the test is passing so we completed our first test in this test module we can organize and group tests with another function it's called as this cripe and just like the test functions we can provide description for the describe function so as first parameter we can say this is user ranges iteration and for the second parameter it will take the the function again and in this function we can add the test functions in it so moving it inside this part again saving this one and in test console we see this is user registration tests and we also see the descriptions of each test now before finishing this up let's change the structure of our code a bit now let's keep the index.js in the root folder but let's create a folder as src and moving this fjs in it so our tests failed because the relative pad is updated but it's not saved saving it and we see all tests are passing now we are going to save the user to database for that we are going to use another library it is sqlize this is an orm library which means we will have object definition on our code but this will be handling the database connections and corresponding table creations and necessary sql queries for us so we are going to install this one but also we are going to use a sqlite for our development so we will install both sqlize and sqlite 3. so going back to our console running npm install c equalize and skew light tree now let's create a folder under source let's call it config and here in this config there will be database connection functionality so adding the module database js now we are going to use sqlize and we will require it with sqlize then we are going to create our own sqlize instance let's give lowercase equalize name to our instance and it's going to be new sqlize now we are going to provide a couple of parameters to this one the first one is the database name let's call it hoxify this is our client application name and the next parameter is the username let's say it is my db user and the third parameter is the db password let's say it's something like this one we are going to connect to sqlite so it doesn't really matter what we are setting here if we would be connecting to something like postgresql or mysql we will be adding the real user and password for those databases and the third parameter is an object [Music] and in this one we will set the dialect [Music] and we are going to be using skewlite and we are going to store the database the sqlite database so we define the storage it will be stored in the root directory of our application and let's call it as database sqlite and let's export this sqlize instance we have here so we created our database connection instance now let's define our model the user object so here under src creating another folder let's call it user and in this user folder creating the module user js this will be our sqlize model instance and that is actually the definition of our table in the database and also the object the json we will be passing for the user object we will define the user so we will first again get the sqlize with the uppercase the the library one [Music] sqlize and the other one is our own sqlize instance and we will require it from one folder up config and database now we are going to create the model so first let's get the model as a variable like this sqlize model and we will have a class of user extending model so this is the basic structure of a sqlize model then we will call the user object initialize method that is coming with this model and in this one the first object this is going to be object attributes and second one is the option so this is the attributes and this is the options we will provide both of them for attributes we will have username field and this field will have a type and the type of this field will be string [Music] then we will have email field again this one will be also string and finally we will have password field and this will be also string so we have defined the attributes then for the options object we will pass our own sqlize instance then we also set the model name which is for our table name we will set it as user and we will export this user model from this module so we have database connection module and we have user model instance now we are ready to add our tests again going back to user register spec now we are going to add one more test again copying this one and in this one it saves the user to database so again we are posting this user object to backend then this time we don't need to do anything about the response to removing that one and instead what we are going to do is query the user table and here we will query query user table and then we will do assertions based on that result so what we are going to do is import the user model to this test module it's under src user user so here we will run user there are built-in functionalities like find all find one this time let's find all let's run find all again this is an asynchronous function so we will get the result with the done function and it will be providing us the user list and [Music] in this then we will do our assertions so we expect this user list length to be one because we inserted a user and moving this down inside here so that after these async operations we will tell just that we are done so we are ready but we are not running the test so here running the test in console for now all tests must be working but you will not see the the tests results because i am committing each changes in each these parts so it's not finding any difference so that's why we don't see the test results so i'm hitting letter a to run all tests without checking the git comments so our tests are passing so now i'm saving our third test and here we see a failure in this test and also we have this warning saying that no such table users by the way this is looking for users we are telling this is user but uh by default sqlize is generating a plural name for this table so that's that's fine that's not the problem the problem is the database is not created yet so when we try to run a query against this database since the the tables are not created it's drawing this error to fix this we need to initialize the database so what we are going to do is we will get the sqlize instance we created in our config database and we will initialize the database in each test we will be interacting with the database so instead of usualizing the database in each of these tests we can use adjust functionality we can call a function of before all this is a function of jest and this will be run before any of the tests are run so we will call before all and we will provide a callback function for this one and in this function we will be calling sqlize sync so this will be initializing the database but again this is an async operation so we return it back so just will be waiting for sync operation to be done then after this before all ends it will be running our tests saving again now our test is failing here we see the we are expecting here in this one we were expecting user list length to be one but we received zero so again this is an expected behavior because we are not actually saving anything to database so what we are going to do is in app js we were just returning a response like this one but now we need to save user to database so first we need to get our user model from from user user and we will call user create this is looking for a object and this will be user object and we are sending it in post request so it is coming to this function this request object and we can access the object request body so this request body is actually the the object we are sending here so we get the object and we tell our model to create it so this will be saving it to database again this is an asynchronous call so if it successfully saved it will be calling this then function back and here we can return the success response back saving it now the test is failing again let's check it one more time here we see we were expecting it to be one but this time we received three why is it happening because we have two more tests doing the same thing so we are also adding these users even they are all identical we are still inserting them to database so that's why we are seeing this table to be increased in each test for solution we have to make sure we are having a clean database so each of these tests must be running an isolated reliable environment so the other test shouldn't be affecting the condition it is running at so we must have a db with a predictable state so for this one we will do another before operation and this one is before each so this will be called before each tests to be run by chest again we are passing our callback function and in this one we are going to return again this is going to be an async operation so that's why we are returning it we will return user to destroy the table with the parameter of truncate [Music] true so this will be cleaning the user table before each test so even this one adds something to it before this test to run it it's going to be cleared by this before each function so saving it now we see all tests are passing now let's add one more test again copying this one in this test we will check which fields are written to database so it saves the username and email to database we will check the password later for now username and email check is fine so after finding the users we will get the first one there's only one user here so we get the saved user with user list first item [Music] and we expect saved user username to be this one user1 we we are sending this one and we expect saved user email to be user one at mail.com so saving it again this test is failing because we are receiving a null as a response now let's open the database here we have this database sqlite which is initialized here with this sqlize instance for opening the database we have we can use either the plugin the extension here the sqlite this one or there is another application db browser for sqlite so either option is fine so here in opening the explorer part i'm going to use the vs code extension for opening the database so first let's maximize this screen and ctrl shift p and sql sqlite open database it detects the database sqlite here this one and here sqlite explorer is visible here and when we here click the this icon it's running the query and we see the user table is like this one so the username email password fields are null and this is happening because the request body here is not parsed properly to express to parse the incoming request body as json we need to configure it and there is this library called as body parser if you check some express application codes you may be seeing it to be used there but we are not going to use this one instead we are going to use json parser middleware coming with the express 4.16 so let's use it all we have to do is just call app use with express json so before before saving this one let's close this screen let's close the database and back to split screen now saving this one and here we see all tests are passing so we are storing the data properly in the database let's also check the database one more time to see how is it looking so we have the user password email all start here and we have these created add and updated ad fields and these are automatically added by sqlite and this primary key is also automatically added these date fields are for auditing we can use them or we can ask sqlize not to create them but for user object it's fine and also here in the console we see lots of logs about the sqlize let's just disable it we don't need this much lock to be printed out so here in our sqlize instance creation part we say logging to be false so clear output and we see the tests are still passing let's also do a manual test let's make sure we are initializing the database so in index.js let's get the sqlize instance require src config database and we are going to call sqlize [Music] sync now killing the test console running our application and by the way we moved the app to src folder but our editor didn't update it this way so updating it to src app so that's why we have this error now the application is working and for testing we can use postman or our own client here running our application and opening on browser [Music] going to the sign up page and let's write user one user one at mailcom password password and sending sign up request and we receive 200 okay back and also we see in these logs here the post is properly sent to the backend now also we can verify in this sqlite database in user table gun full screen here we have the user object let's add one more user to user to add mail com password as word so here again running the query and here we see the user is added to database so we are storing the user in database now in sqlite we are storing the password as clean text and this is not something we should be doing if we store a user password we must be store it in hashed version not the clean text version someone access to our database they shouldn't be seeing this password like this one so we are going to hash it so adding our test so in this one it hashes the password in database again we will have the done and let's copy this part now we are going to check the password of this saved user not to be not to be the password we are setting for it so stopping the application and running the test saving it so this one is failing here failing with the object equality we expected it not to be password but it is so that's why it is failing by the way this is printing out lots of logs uh here because of this this structure actually we are running our tests in this promises and in this then function we are not handling the catch block we have another promise here and we are not handling the catch block either so it is failing at this step and throwing an exception and actually it is trying to find the catch block here but since we are not implementing it in the end it is printing out something like this to make sure we our test is properly working we can just change it to to b not remove the nut part and check the equality we see it is properly working but that's not what we are looking for we expect this password not to be this password so let's fix this we are going to hash the password and we will use the bcrypt hashing algorithm we will use this bcrypt library so here it is showing how to install or how to hash passwords so here this is how we are going to hash it so first we have to install this one killing the test console running npm install be creeped so let's run the tests again and we are going to implement the bcrypt hashing opening the fgs now let's get the b crypt from our pcrypt dependency now we are going to call bcrypt hash function and this is going to be an asynchronous one we will hash the user password so we will get it from request body password the second parameter we are going to provide a round so let's see the samples here the salt round is something like this if the number goes higher the possibility of the uniqueness of the hash increases but the amount of time of calculation is also increases so let's go with this one here also we see the round costs so let's go with the 10 so this will be creating and hashed password since this is an async we will again get it with them let's say it will be passing us the hash and in this then function then we can call user create but we are not going to pass the request but this time we will create a new user object let's say we have user and username to be request body username and email request by the email and password will be the hash we generated for this password and instead of passing the request body we are going to pass user to our model so it will be saving this object to database so saving it so all tests are passing in this user creation part we can use uh other javascript functionalities what we are doing here is just taking each field from the request by the one by one and assign it to this user but alternatively we can have a user object like like this one let's say user is and we are going to call object assign and we will add the basis of the object it's going to be empty object then we will add request body to it then we will add password field with hash so even the request button has the password the next one coming this from this object will be overwriting this this one so if you go with this way it would also work saving it and we have also one more alternative again commanding this one out instead of using this object assign we can create an object and we can copy the values coming from the request body with spread operator this is called a spread operator triple dot then request body so this will be taking all the fields like username email and adds to this user object and then we can overwrite password with the hash so we have this parsing error it's coming from eslint we will fix it but we will be using this version of creating object so let's fix the linting error first in eslint config we will be replacing this ecma version with 6 to 2018 after saving this one now the error is gone and saving this test sometimes this couple of saves are conflicting each other and it can lead crash to tests so running the tests once again we are having a problem with the the database file access um we will be providing a solution to this one but for now for a temporary solution let's remove the here database skew light [Music] it's also opened here so it's gone now and running the test again the application will be initialized and it will be creating the database here here we see tests are passing so let's open the database once more to make sure the the passwords are here we see the passwords are encrypted here with decrypt algorithm tdd has three steps first we write our test second we do the implementation that fixes the test and then we do refactor it is one of the crucial part to keep our code clean and reusable so we are going to refactor our implementation now first we are going to move this implementation out of the app.js this is user related operations so we are going to create a user router js under user folder we will require express [Music] and we will have router from express router [Music] now let's copy this part here the app and the user and b crypt we are not using them in fjs anymore so moved this required and the implementation now we are going to define this post with router instead of this app let's correct this part and we will export [Music] this router and in fgs we will have user router require user user router and we will use it in this application like this we will call app use user router so let's save all these changes our test is failing because we just copied and pasted here but the this required pad is wrong now the user is at the same folder with the user router saving it again and all tests are passing now here we have multiple promise we are using promise here for this password hashing then we use the promise here for the user create part now instead of using promise we can use another javascript feature async await so let's do that first we have to define this this function this this callback function we have here for this route with async then inside this function we can use a weight to wait for the result of these operations let's say we will wait for hash by calling this decrypt hash now we don't need this then part so we will be receiving the hash then we will be creating a user now we are going to call the user create here we can call user create await and that's it and then we can return our response back so with this way the code flow is more clear and understandable so saving it so all tests are still passing now let's add one more layer to our application now our router is handling this password encryption and handling the database connection parts now let's move this implementation another module and that module will be dealing with our business logic so let's create user service js so user service will be handling our user related business logic now here for now let's just [Music] export an empty object we will fill that in a bit and let's get the user service we will require it user service now what we are going to do here is we will call user service for now we don't have it but let's assume we have it there is save method save function and we will pass the request body and since it is going to be doing the things like we did here the in asking a weight way we will wait for it to be doing what it's supposed to do and let's cut this part out and in user service let's have save function and this will be async and it will be taking the body and let's paste the implementation now we are not using the user and b crypt here so cutting it from user router and pasting it here and instead of getting the body through request now we can just can just get it like this and let's export this save function from this module so saving all changes and the tests are still passing so here in user router this user save is pointing to our save function we have here so we added extra layers to our implementation and since we have the tests are integration tests which are not focusing to our implementation but more focusing on whole application behavior we are free with our implementation we can move the implementation from one module to another now let's also refactor our tests in these tests we have been repeating most of these the post operations so instead of doing it in each test let's create a function here let's call it post valid user and it will be just copying this part it will be returning the result back for this application post request and here in this test instead of running the same thing we just call post valid user then we will get the response then we expect this part and let's repeat same thing for the others post valid user and for the next one [Music] and for the other one and the last one so repeat it repeating part is extracted from each test so they are more easy to read and understand what they are doing saving them and we see all tests are still passing and let's make them more readable by using once again a single weight we use them in our service and router but we can also use it here so what we will do here is first we will define our function is async then instead of going with then here we will wait for response then we can move the expect after this one now we are not going to need this done part so we can just remove it from this test function saving this one so nothing is broken let's repeat the same thing for the next test we're moving down adding async getting the response with a weight post valid user and removing this part and for the next one again removing done and this one we are not going to interested in with the response so we just call post valid user and we will wait for the result and then we are going to get the user list by calling await user find all [Music] then we expect this user list length to be one so removing the rest of it saving these two also so let's make sure nothing is broken in this way and the next one is also similar to this one again first setting asking for the function and removing done and posting user [Music] listing all users and getting there uh saved user then to save users email and password same thing for the last test and all we do here is expect the password not to be equal okay so saving them once more so all tests are passing so we refactor our application with a better structure and also we refactor both our test and application code with more easy to read way by using async await now we have this database configuration and we use this same database bot in test and application using same database for testing is not right we have to have a separate database for tests so that we can make sure our tests are running in a predictable environment so we will add two more dependencies to our application one of them is the config this is for extracting the configuration to modules other dependency is this cross and this is a cross-platform environment variable settings library so let's install them both we will install the config as a regular dependency so we run npm install config and we will install [Music] this cross f as a development dependency now let's update our pack json scripts now we are running the application with this start script and we run our test from here now we are going to set environment variables for our scripts so we can use those variables inside our code and decide which database we are going to use or which folders we are going to store data or those things are going to be decide based on this parameter so we will use cross f and we are going to set the parameter node underscore f this is all uppercase and it will be equal to for this starting the application we let's say it is going to be a development environment and let's also update the test script with cross f again node m equals to let's say test let's say this so what is the parameter is about so in index.js let's log it so here console log now we can access that parameter let's say f [Music] is process f node underscore f so let's run the application pmstart so here we see the application is running in development environment let's actually move this line to app so that we can see its effect both in the actual start command or the test command again running pmstart and we see our environment is development and running test and here we see the console look coming from the fjs it's the environment of test so we have a variable that can help us to decide our application logic or any different configuration can be based on this parameter now removing this line now we can extract our database configuration like the database name username the other things to a config object the config dependency we installed is by default looking for a folder in our root directory which is config and in this directory it is looking for the this these not m variables we are setting so if we say development it will be looking for a file in it development.json or let's say we set it to dev then it will be looking for dev json so we have two environments development and tests so let's create corresponding json files under this config folder we just created so we will have development json and we will have test json now in this json we will have an object and we can add anything we want to here so for now we have configurations for database and for that we have the database name which was just copying this part i'm going to add each of them to our json file don't focus to this error part so it will be hoxify and username is let's say my db user and password db pass then we will have dialect which is skew light and we have storage which is database sqlite and the logging which is false so we have the configuration object for development now let's copy this one and paste to test json and let's replace this storage as test sqlite so we will be using database sqlite when we are running in development environment and if we are running in test mode then we will use the database test sqlite now we have our configuration files now let's use it in database so we will get the config [Music] by requiring it and then we can access the let's say we will call it db config and we get it config get database so config get will actually mean that config objects database field we will be getting that part so we will be getting this part as db config object and here we can say this is tb config database dbconfig username db config password and db config dialect db config storage and finally db config logging now saving it so the tests are still passing and let's look at the folder here and here we see we have test sqlite it is created for our tests because they are running it is creating this now let's remove all of them removing database removing test sqlite now if we run the application we will see the database sqlite here and if we run the test we will see the test database here so we are making sure we use separate databases for testing environment it's not a good practice to run tests against a stored database like this one and here we see our test is crashing because of operational errors against the database file so a sqlite has a special keyword for storage so we can say colon then memory and ends with column so removing the test sqlite again and let's remove the database also and running the tests now we see all this are passing and we don't see any database file here this is more suitable for testing so that's how we configure our application for different environments we will have two more environments added to our application which are going to be staging and production and we will see how we can use them to prepare our application to deployment
Info
Channel: Programming with Basar
Views: 1,739
Rating: 5 out of 5
Keywords:
Id: dTn_biKznU4
Channel Id: undefined
Length: 118min 50sec (7130 seconds)
Published: Mon May 10 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.