NestJS Testing Tutorial #1 - Unit Testing Controllers

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right guys so we are back with a brand new video and in this video i'm going to teach you guys how to unit test your applications in nest js uh so it's actually very easy to test code in sjs because they have a whole framework that allows you to test your code very easily and so you don't really have to worry so much about you know structuring anything from the ground up a lot of stuff is already pretty much taken care of for you and every single time you generate a controller or a service it will also generate a test file along with it and there's some written code already that it will use to just you know test some certain things so we haven't written any test cases at all but if i actually run uh if i actually were to run the test cases uh it should actually throw some errors for some of these uh for some of these uh test files the reason why is because we actually have some dependencies from other files that we have not configured so that's why i would that's why essentially it would actually throw an error but don't worry about that though what i'm going to do is i'm actually going to generate a new module and we're going to start complete from scratch so even if you have not seen my previous videos that's totally fine as well we're going to basically start from scratch by creating a new module creating new controller and a new service and we're going to unit test that so i'll go ahead and just generate a module and i'll call this let's see so we have customers we have users uh let's create a module called payments so we're going to pretend like this is a module dedicated for handling anything related with payments okay so we have our module and the module uh file does not have its own test uh test file files don't worry about that so i'll create a controller and what we'll do is inside the controller we will uh let's see so in sg controller this will go inside the uh whoops so it'll go inside the payments module and inside the payments module we'll have a folder called controllers and then we will generate the files inside a subfolder inside controllers called payments so that way because in case that we have other controllers that might belong to the payments module we want to put everything inside its own folder so it looks a lot cleaner okay so uh right off the bat you can see that we have this payment.controller.spec.ts file you can name your test files however you like but you need to make sure that it ends with the dot spec.ts extension or test.ts extension okay because that's how jess will pick up those files okay you can also configure just to pick up certain files based on certain extensions but we will not cover that in this video okay so before we actually write any test cases it's important for us to understand how this whole testing framework is all set up because if we don't understand how that works we're gonna have a hard time figuring out how to actually test so let's go ahead and get started so if we look at our test file you can see that we have a very similar uh describe and this is coming from the actual just framework itself okay so we have describe block and then we have the name of this describe this uh this test suite okay and then we have the uh inside the callback function we have our declaration so we are declaring a variable calling a controller and the type is payments controller and then what's happening here is we're using this before each function i like to call it a hook and basically before each will call this callback function before each test okay so before every single test what will happen is we're going to go ahead and create testing module and basically what this does is it creates you can think of it like creating a mock module and you're going to provide or not provide but you're going to list all of the dependencies that this testing module has so for example our payments controller uh our payments controller class it requires well first of all this module is going to require the payments controller okay now right now we don't have any other dependencies we only have a controller so this is all it's going to do and once that's done it's going to compile everything and once that's compiled we can actually retrieve the actual controller from the module by calling the get method and it's going to go ahead and give us the instance of the payments controller so what happens then is we can actually call certain methods on that payment controller so for example if you actually take a look at controller dot right now nothing's popping up because we don't have any methods okay but if we were to create some methods inside the controller so let's go ahead and do that let's just go ahead and do gets and we'll do get payments okay and if i were to call a method on controller using the operator you can see that i get payments appears so we can definitely unit test get payments now one thing that i should mention that is also very important is that when you call module.get you're really actually trying to retrieve the dependency from the actual container because remember nest js is based on dependency injection so there's a container that has all of the dependencies okay so that way when you it will declare and initialize all of its dependencies uh during runtime and then you can retrieve those dependencies from the container itself using dependency injection using those decorators that we have used in the other tutorials and basically when you call this dot get you can see that this method actually takes in a type or a token okay and you can actually either pass in an actual token to retrieve the actual dependency or you can pass in the type so you can see right over here what we're doing is we're passing in the type so it's payments controller so what happens underneath the hood is it'll actually look for the correct instance and it'll give us that controller so um let's say for example a good use case would be let's say if we have two services that our payments controller has as a dependency and let's say uh if we use token injection where instead of like just using dependency injection by simply doing something like private service and then type annotating it with the service class we use an actual token to inject it in so when we actually set up the testing module instead what we do is we'll actually reference a token instead of the actual type and like i said we'll look at a lot of examples so don't worry so right now let's go ahead and write our very first unit test because right now we haven't written any just yet but before we do that let's actually add some more logic to our controller so just for demonstration purposes what i'll do is i'm going to go ahead and uh let's see we're going to go ahead and use the request and response object so just that way we can cover how you can properly mock certain objects because there are sometimes where for your controllers you might need the actual request object and the response object so i want to show you guys how we can handle that so let's go ahead and type annotate this as the request object with the request interface coming from the express library and we'll go ahead and do the same with the response object as well very uh standard a way of handling requests when you come from an express background okay and i'll show you guys lots of examples so don't worry so much okay so let's say get payments is going to be a function and it requires uh a query parameter okay so query parameters uh can be accessed through the request.um query okay query object and over here is where you can access you know any uh parameter so let's say for example inside the query parameters we let's say we have some pagination happening over here and we want to get the query parameters counts and uh let's let me let me do this on accounts and then we want to get the current page so count would basically be uh the maximum number of entries or records that we want to return back to the user and the page is the current page that we're on okay and if you're also if you're interested in learning how to uh integrate pagination in your application definitely check out one of my videos on pagination i covered that with using express and react okay so happens both on the front and and the back end so let's say for example um we're going to go ahead and just manually get the uh get the query parameters from this request.query object now obviously there's also the query decorator that you can use but like i said for demonstration purposes i'm just going to show you guys how we can do this okay so uh let's just say for example if there's no count or there's no page query parameter we'll go ahead and we'll send back a response of 400. okay uh and we can also uh i'm actually let me do this let me do response status 400 and send uh and let's just do a message uh missing count or page query parameter okay so uh we covered that case now let's say if both of these uh parameters are are there now we can add additional checks but like i said let's just keep it simple so if we'll just assume that the query parameters that they provided are valid so we'll just go ahead and say uh let's see response.send 200. so how do we unit test this now okay so what we'll do is we'll go inside our payments.controller.spec.ts file and what we'll do is we'll essentially well first let me just delete this uh this should be defined actually i'll leave that alone uh because it's good for debugging purposes sometimes this might actually be undefined because it failed to actually retrieve the module or retrieve the controller from the action module so it's good to have that around let's go ahead and um let's go ahead and test out our controller so it should return a status of 400. and what we're and when you when you're writing your unit tests or test cases in general you want to be very descriptive but you also don't want to write too much so what i like to do is i like to actually create a new describe block and then i can describe what it is i'm testing so we're testing the get payments function and inside this describe block we can have all of our test cases okay so you might have more than one test case for a certain uh function for your controller so we're going to test the uh response of get payments and we're going to make sure that it returns a 400. so the way we do this is now we're going to go ahead and reference our controller and we're going to call it get payments okay now right now we need to actually pass in the appropriate arguments for this get payments uh method so we can see that in get in the controller get payments requires two parameters now this requires us to actually uh mock out the request and the response object and i'm going to show you guys how we can do that so what i'll do is all the way at the top inside you can either do this outside of the describer inside but i'll just do this inside what i'll do is i will actually use a let because we might actually modify the request in the response mock later so i'll call this a request mock later on after we finish uh putting in the appropriate parameters we're going to go ahead and actually pass this into an actual request object okay because you'll you'll notice that if i actually try to pass and request mock it's going to say that this isn't the correct type okay so uh first let me just create the objects first and we'll leave them as empty okay so request mock and like i said i expect you guys to have some basic understanding of guest okay so you're going to see right over here argument of type object is not assignable to type request blah blah blah and that's because uh we need to we need to actually uh cast this into an actual request object so what we can do is we can actually so if we actually try to just cast as a request using the as keyword it's going to expect us to populate the entire object with every single required parameter and we obviously don't want to do that because there's a lot of parameters that are required for the request okay so we don't want to do that so what we can do is we can actually just cast this as an unknown and then as a request okay so this should actually uh yeah the error went away this is just a different error saying that the the request mock is never reassigned so use const for now i'll just uh uh leave this as const okay and we'll do the same thing too for request for response mock so we'll annotate this or type annotated as unknown first and then we'll annotate it as a response and we'll import the interface from the express module and now we can see that uh the errors go away and we only want to focus on providing or populating the object with the parameters or the properties that we actually need so for example we need to make sure that in the request object we have the query object populated so inside query we can provide an empty and empty object okay now for the response uh we need to make sure that we include the correct uh methods uh for we need to include the correct methods uh that we are calling for the uh on the response object if we don't it's going to tell us that this uh this response object does not have that specific method on that object so you can see that in the code it's actually gonna be a little bit tricky because uh we're calling dot status and then we're also calling that send and then we're also calling that send after dot status so we actually have to be a little bit careful with this okay uh but it seems like whenever you call status it's actually going to return the actual or it's it's going to return the actual uh original response itself but we'll have to play around with this just to ensure that it works so first let's go ahead and mock the status or status method on the response object so status is going to be a mock function so uh we're going to set the value of status to be a just fn so we'll just go ahead and take a parameter and just return a parameter likewise for send we'll do the same thing uh so also just do just the fn so what we're so with just fn this basically is going to give us an actual mock function and when it comes to testing your code you don't want to mock the function that you're testing so in our in other case in our case we're testing the get payments method so we don't want to necessarily mock get payments we want to actually call the method but we want to mock any necessary functions that get payments depends on in order for us to actually ensure that git payments is being tested so oftentimes you might call a database you might call some other api you don't want to actually call those actual methods uh when you unit test when it comes to end-to-end testing you do want to do that but unit testing is mostly focused on testing a single component and ensuring that that function uh gives a certain output based on some input so and that requires you to not call the actual database or some kind of api and instead what you do is you provide mocks of the response of the api or of the database and you can write certain assertions based on that response now let's go ahead and uh write an assertion so right now when we call get payments we should assert that response is going to give us back a status of 400 and it's going to call dot send giving us this this object okay so the way we do that is we can go ahead and say expect and we can expect the response mox.status function to have been called with 400 now let's save that and let's go ahead and test this out so i'm going to type yarn test and i'm going to go ahead and type payments so this should actually create a filter so we'll look for the payments control.spec.ts file uh so let's see yeah perfect so this should pass uh let's see so okay so it's sayingresponse.status.send is not a function and like i said the reason why this is happening is because uh status itself is going to return an actual object um so it's a little bit tricky i think what we could do is we can probably let me actually see something real quick i wonder if you can call yeah so you can still call status even after you set the status which is a little bit annoying but what we'll do is we'll just simply uh for status we'll just simply uh let's see seems like you won't let us do that as well okay you know what i'll do uh i'll just for now for the status we'll go ahead and just mock out send again okay so this should fix it so uh yeah when we call that status uh i'm not sure why it does that but i think uh actually you know what no this is uh this is wrong instead what we need to do is we need to we we definitely to mock the function set itself and the return value is going to give us back the send object there we go perfect and i'll just change the variable from x to y for this send okay so this should fix that issue so let's try this again okay perfect so yeah so status is going to obviously take in a parameter so you can think of this x parameter as the actual status code 400 and the return value is going to give us back uh the response uh so since i can't actually just you know since because of because uh this is block scope i can actually reference response mock outside of that uh let's see like now i can i don't know we'll leave it alone for now uh but okay so the the res so the object that status is going to return is going to be an actual uh response okay uh and i'm not going to go ahead and populate every single thing so i'll just go ahead and just manually leave center right over here uh and let's say like i said depending on what you call after status is real it's really up to you so you might call other things after dot status so you need to make sure that you mock it appropriately okay but you can see that uh you can see that it the the assertion passed okay so we expected response mock.status to have been called 400. so the nice thing about jess is underneath the hood it is smart enough to actually replace the actual call and we it can keep track of all of the mock calls that's being happened so just will know that we're actually invoking a mock function okay and we can assert what it is being called with so for example response mock dot status we can assert that it has been called with 400 okay and it did okay it was called 400 because we didn't have the appropriate count and page uh query parameters in the request in the crest.query uh uh object and that is why it returned 400 let's go ahead and write one more session so uh what we'll do we'll go ahead and say expect and this time we're going to go into response mock.status and we should be able to [Music] let's see let's see so it seems like it's not going to let us get the actual send it's not going to actually let us get that send uh property so it's not going to let us do that which is kind of annoying um because let's see so i'm trying to think of how we can actually uh do this i think another thing that we could do is we can actually i think here's what we'll do um here's what i'll do i'll just do a cons status response mop just for now and this status response mock will just basically be similar to the response mock okay so instead of uh yeah so what we'll do instead is we'll return status response mock so let me just delete that okay so what's going to happen is it's going to return status response mock and it's going to call the send method or the send function on status response mox object so now what we can do is instead we can go ahead and write an assertion for a status response mock dot send and we can expect this to have been called with this object over here okay so i'll go ahead and do to have been called with that's another object and let's go ahead and write our test or run our test again and i'm going to use the test watch script so that should run our run our test in watch mode so whenever we make changes it will auto rerun the test again and there you go you can see that uh it passed and if we were to just negate the value let's say for example if i were to just change this to parameters we can assure that this test should fail so this is a good way to avoid i'm pretty sure there's like an esl plugin that you can use to make sure that there's no like there's no uh false positives uh but yeah you can see that right over here it fails when we just change the value from parameter to parameters and if ours changes from 400 to 401 it should uh should fail let me just rerun that again speak to that so quickly there you go you can see that it expected a 401 but it gave us back at 400. okay so hopefully that makes sense when it comes to testing um let's go ahead and do one more example so i'm going to go ahead and write one more test case and this test case should test to ensure that it returns a 200 whenever um both when both parameters are provided so should return a status of 200 when uh query params are present okay so that should do a good job at describing our test so let me go ahead and uh so here's what we're going gonna do we're gonna have to modify the uh mocks so uh request mock query i think we should be able to just modify that uh yeah yeah even though it's a constant we should be able to just modify the internal contents we just cannot reassign a value to request lock that's going to be an error because it's a constant okay but nothing's going to stop us from actually modifying the internal content of it so what i'll do is i'll go ahead and provide the account so we'll do that and then we'll do page one oh it's going over here oh it has to be a string because it's a query it's a query string okay cool so that should update the value of request mock dot query and now we can just call uh get payments on the controller pass and request mock we don't need to do anything with response mock so we'll leave that alone and now when we call this method we can assert that response mock dot send and like i said you have to look at the controller and you have to look at what is happening and it also might help is if you follow a practice called uh tdd which stands for test-driven development where you write the test case first and then you implement the actual details so it gives you a good way to uh to so you can basically base your implementation on the test case so when you write the test case you can ensure that this is supposed to give back a 200 or 403 and then you can write your code to fit that criteria okay it's actually a practice that many companies follow along but we can ensure that this response is going to give us back to 200 so let's go ahead and do response mock dot send yeah it should be send yep and we can go ahead and say 2 have been called with 200. now if i save let's go ahead and uh let's rerun it again oh whoops didn't need to do that did not mean to run all the tests just meant to run payments there you go you can see that it returns a status of 200 and that has passed if i were to uh pass 201 it should fail and there we go we can see that it expected a 201 that we received a 200. so that is how we can unit test our controllers okay so hopefully that makes sense now in the next episode what i'm gonna do is i'm gonna create a part two uh because right now we only tested a very simple case with a controller and we don't have any dependencies but what we're gonna do is we're gonna create a service in the next episode and we're going to have that service do some kind of computation and what's going to happen is i'm going to show you guys how we can mock up the service to ensure that we are practicing actual unit testing so we're not calling a bunch of other dependencies so i'll show you guys how to do that in the next episode
Info
Channel: Anson the Developer
Views: 24,480
Rating: undefined out of 5
Keywords: nestjs, nest tutorial, nest framework, nestjs unit testing, unit test
Id: lih9VT0IZ7U
Channel Id: undefined
Length: 25min 59sec (1559 seconds)
Published: Thu Nov 04 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.