NodeJS Express Test-Driven API Development (TDD)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey how's it going welcome back to channel in this video we're going to cover test driven development and specifically how we can use that to create apis in node.js [Music] we're going to be using the express framework and we'll also be using just as our test runner now it doesn't really matter what framework you're using the important thing to take away from this video is the principles behind test driven development in theory if you understand what i'm doing here you should be able to take the same ideas and move it over to whatever framework you're using so for example maybe you're using sjs as you've another thing i've been doing in my recent content you should be able to take the stuff you learned from this and move it over to an sjs app or whatever other framework you're using so we'll just jump right in like i said we're going to be using express.js and specifically i'm going to be using the express generator it's a great way to create you know a basic boilerplate express application and i've already done this i ran the express generator in in my desktop as you can see i have an api tdd folder here which i'm going to open up in vs code and the way i generated this was i just ran npx express generator dash dash no view and then the name of my project if we take a look at what's generated there's not really much here just got your basic express app it's got your typical express middlewares like body parser and such cookie parser nothing really special about it and then you got some initial routes we've got an index route and a user's route which just responds with a string so it's not really quite real yet but there's enough there for us to kind of get started with all right so first thing i want to do is npm install because we got some dependencies in here after you do that i also want to start installing my my dev dependencies which in this case is our test runner jest so npm install dash d jest and then we also want to install super test which is going to be our framework that we're going to use to test apis specifically all right once that finished installing you should see that it got added as dev dependencies and then we should be able to add a new test script here which simply runs just and then i'm going to add the dash dash watch all flag here so that anytime my code changes it's going to rerun the test for me and that's something that's really important where you're doing tdd because you want to be able to have your tests running side by side with the code as you develop and implement it so in the terminal just to test this out i'm going to run npm test and we can see that just is running here and it didn't find any any tests because we didn't write anything yet so i'm going to go ahead and create a new app.test.js file i'm gonna just add a basic test here just to make sure that our setup is running properly right so i have an empty test here it's not inserting anything and we see a passing test so first thing we're going to do is we're going to include our dependencies so we just installed supertest so we're going to ins we're going to import that as request all right and then we also need to import our application so real quick explanation of how to do tdd or test driven development the basic idea is that you first write your tests and you they would be failing and then step two would be to start implementing until your tests start passing and then step three is to refactor you know improve the code and then maybe even write more tests from that and then you kind of just go in that circle until eventually you end up with a fully implemented application you got your all your stuff is working and at the same time your tests are already there so the end result is a a working piece of software and be something that's already well covered with uh tests so it really is that simple so that's exactly what we're gonna do uh like i said we're using super test if you haven't used super test before highly recommend going on their github readme pretty much details most of what you need to know but the basic idea with that is you're able to pull in your express application and then you can sort of simulate calls with it as if you know an api consumer or a user is is making calls to your api and in addition to that you can start adding assertions to it so for example you can assert that you know the content type is json you can insert the status code you can assert the uh response body and so on so let's jump right in uh first let's talk about what we're what we're trying to build right so imagine that you're trying to create a an api for a to-do list application so that should be pretty easy to imagine right you want to be able to uh have an api that returns your to-do items you want to be able to create a new to-do you know basic crud you should be able to create read update delete in this tutorial we'll just do a subset of that crud so we want to be able to uh get to do's and get specific to do's and then we also want to be able to create to do's so let's go ahead and start writing tests for that so what i'm actually first going to do here is i'm going to create a describe block this is actually optional but it's a good way to group your tests because your your application is going to have multiple things in it right so in this case i want to group the test specific to my um to do stuff that i'm going to wrap in this describe block all right so i'm going to move this in here so maybe i'll call this to deuce api and you'll notice in the terminal it kind of groups it by the describe and then the individual test let's first start thinking about how we want our api to behave so we talked about that perhaps we want to be able to do a slash to do's and that returns a an array of to-do's right pretty basic um we also want to be we probably also want to be able to uh get a to-do by id so this should return a specific to do by id we said that we want to be able to create to do's so this creates a it should return a newly created to do what else can we do here with creating stuff you probably want to do some validation right um let's say that it validates uh it's a request body and we'll improve this in a little bit i'm just kind of laying out what are the tests that i want to do for my api and remember right we didn't implement the api yet we first want to design it in our head and then write the test for it it does mean that you need to have some basic fundamentals when it comes to writing apis right so you ideally should already know what http verbs you're supposed to use you know so for example typically you would use a post for creating you would maybe use a put or a patch for updating you would use a delete for deleting and so on so it's important to have that initial fundamentals as well as how would you typically write your api endpoints so that's probably the only real thing you need to have or you need to understand up front another thing that i want to actually add over here is doing slash id returns a 404 if we didn't find it right so that's a pretty typical scenario in any api that does get by id 404 if not found all right so that's a pretty good set of initial tests you absolutely if you're following along you're you can probably do the rest of the crud as an example for you to practice on so maybe do some tests for updating and deleting all right so let's start implementing these tests and remember what we want to do is actually write tests that fail because we didn't implement them yet so with supertest you can sort of simulate uh i think of it as like a fake request by doing it like this so we're gonna do return request and we're gonna pass in our application in there and then from here you specify what http method you want to run in this case we're going to do a get on slash to do's and let's think about what are the what are the some of the stuff that we want to assert here first of all i want to expect that uh the content type is json and we want to be able to assert the status as 200 so we can do expect 200 and then let's do some assertions on the response itself so let's say that our array of to-do's will have a name property and a boolean property for whether or not it's completed so let's do then here so you'll notice that we're effectively working with promises and within this then callback is where we can start writing some assertions on the response itself so for example with jest one of the nice things you can do is create assertions like this and we can assert that the response body equals something right we can say like it should equal an array but to kind of make this a little bit more useful let's let's say that we want to expect a array containing and within that array we expect that to be some sort of object like we said which has this shape has a name and we want that to be a string and a completed which we want that to be um any boolean again this basically just says when i call slash reduce with http get i should get back some kind of array that contains you know any number of objects with this shape it has a name which is a string and completed as a boolean all right so i think that's it for this test i'm just going to go ahead and copy some of this because some of this logic is is reusable in this next test so we want to be able to do slash to do slash id and get back a object right so it's basically sort of like a subset of this request if for example we did slash to do slash one we still want to expect json json content type we still want it to be a status 200 but in this case we just want to get back the specific object that has that id so we're gonna remove this array containing wrapper and we're just gonna do expect to get back an object that has those properties it has that schema all right so that's pretty basic let's do what's next we said that we want a 404 if it's not if it's not found so in this one we can just do return request kind of similar thing you want to do a get slash to do's slash some id so in this case we we want to provide an id that is going to result in a four or four you know uh let's just say that you know something with a crazy 999 value like that is going to result in a 404 and we can do expect 404 we're just going to keep it simple like that you can also assert the response body if you want but the default express application that we have here from express generator just has a pretty basic uh built-in error handler which returns a an html page so we're just gonna keep it simple we're just gonna assert the the status itself you can improve this later if you implement a new error handler in your express app all right next we want to be able to do a post to create a new to-do so we're going to do return request app dot post and we're going to do to-do's right so in this case we're still using slash to do's but we change to specifically the http post verb so one thing that's different here is we can do a send which accepts an object which is effectively going to be your request body so in this case we probably want to ask for a name for the new to do you know maybe it's um let's say do dishes all right and we still expect it to return some form of json but typically with creating resources you want to respond with a status of 201 right 201 created and typically when you do a uh create request apis would typically return the newly created entity so we can maybe copy this then block except that we're going to make it more specific right so in this case you want to be able to expect that the name specifically matches this name right so it's not just any string anymore and specifically we want to say that initially this should be false now finally we want to do some validation later on so in this case we want to be able to assert that if let's say that if you gave a number for the name it's gonna return a status for 422 or maybe 400 whichever one you'd like um so let's do return request again app we're still going to do a post on slash to do's and we're still going to do a send but in this case we're going to send a a value that our server does not like so let's do maybe just one two three and we expect this to you know error output maybe a status 422 so there it is we have our tests and we're gonna go ahead and start implementing this one thing really cool to call out real quick is ideally all of your tests is initially failing we do notice that our test for the 404 is already passing and that's because we actually never implemented the the route for um to do's so it's automatically giving back a 404 so that's kind of a false positive in this case but you'll see as we implement this you'll eventually see that this will fail and then we're going to make it pass again all right so we've got our tests written now we get into implementing the code itself right so we've got our app.js first of all we don't have any routing for to do's so that's what we're going to do first within the routes folder i'm going to create a new file to use that js and i'm just going to copy this initial structure that we have in our example users router just for the sake of time and we're going to start implementing our routes but actually before i do that we also need to register this router into our application itself so maybe i will just replace this user's router with the to-do's router let's rename this to to-do's router and you see it got updated here and we'll update this to slash to-do's all right now we can go back to our to use router and start implementing so since this is just a basic tutorial i'm just going to create an array of to-do's you know you can imagine that in a more real realistic example this is probably going to be pulling from a you know a database but in our case we'll keep it simple we're just going to have an array an in-memory array of to-do's so this might have an id let's say 1 and we said that there's going to be a name property and a completed property right so in our case we just have one item one to do item all right so let's start implementing so our splash our slash here is really representing slash to do's right it's effectively that because remember in our app.js we're sort of registering this router under the slash deduce path so really everything that gets defined here every route that gets defined it has this slash seduce prefix if you think about it that way so if you take a look at the the logs on our failing test right it says that get slash seduce is meant to return an array of to do's right now it's failing on the content type because we're simply returning a string so we can change this to json so that part it makes that step past but then it says that you're supposed to return an object that looks like this where you're returning a string so if we return to dues here all of a sudden our first test passes and actually we should probably add in our tests that this has an id property which is a number all right so still passing next let's try to get our second test here to to pass we're gonna do router.get so we're saying that this is by id if you know expresswell you know that you can specify url parameters like this and that's going to be in a request object so as i have this typed out notice that our 404 test is now failing as we expect it to because it's it's now an existing route so it's not 404 ring because of that um it's failing because we never returned a 404 response within this route handler but we're not quite there yet let's first work on again this second test we want to be able to return a specific to do by id so this should be pretty simple you should be able to do to do's dot find and we're just gonna do for each to do if that's due id equals equals request request.params.id and we should probably translate this to should cast this to a number because that's probably a string all right so this should return the to do with the matching id that you passed in through the url so maybe let's change this to found to do and we're simply just gonna do rest.json that to do that was found if you were doing this for real write you would probably do some kind of database query here to find your to do in a database all right so notice that our second test is now passing now let's get back to the 404 what if that to do that we're looking for is not in there so we want to add some logic here of if we did not find to do we want to return a 404 here what i'm actually going to add here is there's this library called http errors that i think is is pretty useful in express apps so we're going to install that http errors all right now that that's installed i am going to import it as create error equals require http errors and again if you know expresswell you you probably know that you typically would just kind of forward the error down the that application and you do that with the the next function here so usually you would do something like next and then you pass in your error object here and then something else down the line in your application if you have your own custom error and handler for example is going to catch that and it's going to respond accordingly express like i said does have its own sort of built-in basic error handler so we're just going to utilize that i don't really think it's worth creating a custom one for for this tutorial but in this case we're gonna do next create error and we want to say uh you know not found and i should probably return there and all of a sudden our third test is passing right so looks pretty good to me and then our last test one second to last test here is doing a post so how do we create to-do's so we're gonna do a router dot post on slash to-do's let's write our inline function route handler and what does this do first of all you should know that the the request body lives in request.body so you can do something like this and then imagine that we're trying to just take that request and add it to our list of to do so we probably want to create first of all we have to create our new our new to-do object and we should probably give that an id we're just gonna do you know we're gonna try and simulate an auto incrementing id we're just gonna add 1 to the length and the name is going to come from the body itself and then we're going to initialize completed to false right because we assume that you're adding a new to do it's not completed yet then we should probably uh update our array so we're going to do a todos dot push new to do and then finally we want to again respond with json and we're going to simply return our new to do actually i made a typo here guys this should just be slash right uh remember because we're starting from slash seduce as a prefix i shouldn't have typed slash to do's over there and now that we have that fixed um we're almost there the only thing that's kind of missing is that it's expecting us to use a status 201 from the test so i'm just going to add status 201 and finally our second to last test is passing now we've got one more test and i actually need to refactor this because i made another mistake here this should also be posted to use i'm going to fix that real quick in our last test this should be post and back into the code we said that we want to do some validation for example if we pass in a number we expect it to uh kind of error out and say that you know we got we want to return a status four to two so kind of similar strategy with what we did here is we can do something we can add a basic validator here like maybe we can do if the type of [Music] body that name does not equal string simply return a create error of status 422 let's just say validation error our tests are passing right so we've got a completely working api the tests are passing and from here you it's up to you where you want to go you know at this point you can further enhance your code you can refactor you can add more tests for whatever other features you're trying to support in your api right so like for example what are what are some things we can clean up here um you know some people like to move these inline route handlers into a dedicated controller file and then you can also unit test those individual controller methods because technically the test we're doing here is really an integration test right so you can further enhance this by doing some unit tests you know just unit testing these individual functions what else can you do you know you can bring in a more sophisticated validator so maybe you want to use joy or something similar where you can do schema validation and the big idea is right our test is not necessarily testing the implementation details if i were to introduce joy here to do my my validation check it should still ultimately satisfy what we're trying to do here where if the validation fails we do a four to two so this doesn't care about the specific implementation detail that's kind of up for you to decide um similarly maybe you were doing this in a real application you know you likely won't have an array of in-memory to deals like this so you could incorporate a a database maybe an in-memory database or you could have a mock database it's really up to you right and again you kind of just go in that circle of write tests implement get it to pass refactor write more tests implement refactor and so on and by the way just for for sanity's sake right let's go into uh let's test our application let's run this i'm gonna do i'm gonna do npm start start my application and then i'm gonna go to insomnia and create a new request here we know that this is running on localhost 3000 let's just do a send here to see there's our application and then when we go to slash to do's we get back our array of to-do's let's go ahead and do slash one and that gets back just the one to do item oh let's do let's try the 404 right so if i put in a non-existing id here it's going to status 404 not found next let's test our post we said that we want a json with a name right if we do do dishes i send that it has the do dishes and to check our validation logic by passing some numbers here and send we get status 42 right so that's one of the benefits with test driven development right is i was able to effectively implement my code and i never really once had to go in to insomnia or postman to manually test it i'm effectively validating that my application is working by getting my tests to pass so that's where test driven development is really nice right you end up with a fully working product a fully tested product and then you can kind of just keep refactoring and improving it but yeah there's the basics of test driven development with apis and like i said the important takeaway here is the the principles right not necessarily the specific testing framework or test runner right if you're using mocha instead of just and if you're maybe using sjs instead of express a lot of the fundamentals here still applies there you might just need to change a little bit of stuff so for example you can actually also use supertest in an sjs application right so a lot of the stuff we did here if you were using nest as your node framework then you can still move a lot of these ideas over in that space alright so that's it for today guys really quick video hope you enjoyed that one definitely tdd is something that uh i know i'm personally still trying to get better at but you know if you know the basics of doing it like this and you have these uh base fundamentals it's really it's a good thing to have in your in your toolbox all right with that said hope you have a great day and uh let me know in the comments if you have any feedback questions recommendations you got any content you want me to cover in the future let me know otherwise i'll see you in the next video
Info
Channel: Marius Espejo
Views: 7,931
Rating: 4.9111109 out of 5
Keywords: tdd, unit testing, jest, tdd tutorial, test driven development, tdd vs bdd, node js, express js, express js tutorial, express js crash course, express js tutorial for beginners, node, node js tutorial, nodejs microservice, node js express, node js express api, node js express js tutorial, nodejs express testing, integration testing, javascript, nestjs tutorial
Id: M44umyYPiuo
Channel Id: undefined
Length: 32min 21sec (1941 seconds)
Published: Sat Apr 03 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.