Mocking a Database in Node with Jest

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video i'm going to show you how to write automated tests for an http server in node by mocking the database using gestmarc functions in a previous video i tested an express api that creates a new user by making a post request to a user's endpoint passing in a username and password and that should end up storing a username and password in a database and in that video i skipped over everything that had to do with the database and just focused on the http stuff now in this video i want to pick up where i left off and actually test that app.js sends that username and password to a real database but i don't want to test that it actually stores it in a real database what i'm going to do is mark the database and test my mock to see if it would actually store it in a real database so before we get into the implementation let's think about how we could actually test that the username and password gets stored into a database we could write an automated test that makes a post request to our server to create a new user and the server might run some internal logic maybe it validates the username and password and then it will store it in the database we could then write an automated test that queries the database directly and checks that the valid data got stored into that database or we could then write another http request to maybe try and log the user in and if the login is successful we know that everything's working because the user should be able to create a new account and then if that's successful they should be able to log in successfully what if on the other hand i just want to test each piece of my app independently i just want to test if the http server part of my app is working without testing if the database is working or without testing if the internal validation logic is working in a previous video i showed how we can separate the server side logic from the database logic using something called dependency injection you can use this now to actually mock the database and just test the http server independently so when we set up the app we can pass it a mock database and the purpose of a mock is to test the interactions between different parts of our application so what i can do is test that my http server calls the correct methods on my database file without actually having to test the database directly as long as it calls the correct methods and passes in the correct data then i'll have some level of confidence that if i instead inject a real database into the http server file that everything will work correctly if it works with my mock database hopefully it will work with my real database as well so we're going to test the interaction between my server and the database not actually the database directly so what i'll do here is modify my app.js file so i can pass in the database so in my tests i can pass in a mock [Music] database [Music] so this is basically the same setup as my previous video and if you haven't watched that i suggest you watch that first but basically the app.js logic is now wrapped up inside of a function where we're going to hand it the database that it's going to use and in my production code i have a database already i have a mysql database and for now let's just assume that this file is completely tested and i have tested that these functions work correctly with the database that if you were to call create user it would actually insert a new user into a mysql database so my production code is actually going to pass in the correct database but in my tests i'm going to pass in a fake database and test those interactions and really what we're trying to do here is pass in an object where we have a create user and a get user function because that's all the app is expecting it's an object with these two methods and we could create some fake functions here so i could create a mark create user function and pass that into the database and then write some tests in here to make sure okay was a username and password actually passed into this was this function called maybe just one time and not multiple times by accident if i return a value here let's say i return a fake user id was that value then used by app correctly so it's just testing the interactions with my fake version of the function so that my real version of the function should work so i just want to test interactions with my fake version of the function which will give me some confidence that it will work with the real version of the function now we can just set up these mock functions directly in the code like this and just handle all of the logic ourselves but since we're using jest there is actually a really nice way of creating mock functions using jest.fn so i can create a new create user function using justfn and this will actually keep track of all those things that we might want to test so it will keep track of the number of times this function was called it will keep track of the parameters that were passed in it will allow us to specify some sort of fake return value so we can test it actually uses the return value of the function correctly and a whole bunch of other stuff and i'll leave a link to the documentation on just mock functions in the description but this is what we're going to use for the create user and the get user method in our fake database object here and because i'm using es modules i actually have to import the just global object here so i'm just going to run the tests that are already in here to make sure everything's working before i start making new tests to test the interaction with the database so everything's still passing from my last video so now what i want to do is test some of these interactions so when a username and password is posted to the user's endpoint i need to make sure that that username and password is actually passed on to the databases create user function [Music] [Applause] so here i'm just making that post request with supertest and i'm sending in a username and password and the first thing that i'm actually going to test is that the create user the mock create user function uh is called so calls.length only once because there's a chance that maybe i messed up my code and it calls create user multiple times we'd have multiple users that wouldn't be good so i'm just going to verify that this function was only called once and again i'll put the documentation link in the description but basically this is just jest's interface for figuring out how many calls there were to this function so if i run this test again we'll see that it fails because this function was called zero times and i was hoping it was actually going to be called once so back in my app.js if the username and password were passed to the post request i need to check that the database dot create user function is actually called i'm just trying to make this current test par so i'm just going to call it without any arguments and this now passes and this is again just testing the interface so if i pass you a database object will you actually call that function and the next thing i want to test is that the first parameter that is passed in to the create user function is the username and the second one is the password because in my real database i'm gonna need to make sure that they get passed in in that order to make sure that everything works so the syntax for that is gonna look like this this function can be called multiple times so we're saying the very first time that the create user function is called get me the first parameter that was passed into that function and the first time it was called get me the second parameter that was passed into the function uh so the first one should be username because that's what i'm passing in here is the username and the second one should be password so if i test this now this fails because it was expecting to find username and actually found undefined because i'm not passing anything into this create user function so to make this pass i could pass in the username and password and then if we run this test again there we go it's now passing because it's actually getting the right parameters but this test is not great because it's only testing a single username and password so i actually just want to update this to test multiple username and passwords just to make sure that app is actually passing in the correct data and isn't just hardcoded to username and password so that would still make this test pass so just to give me more confidence in this test i'm going to refactor it a little bit so now i just have an array with different username and password combinations and i'm going to loop over that array and make the post request with each of those combinations and then i'm testing that the create user was only called once and that the correct username and password was passed in each time so if i run this test now it's actually going to fail but not because my production code is bad it's because my tests are actually bad so this create user mock function is going to keep track of all of the state every single time it's called so this is actually going to get called three times so this is actually a bad test here and the second time it's called this should be one and the third time it's called this should be two so i could add more logic to make sure it's called the appropriate amount of times in each loop and and change this from a zero to a one to a two each loop but there's actually a slightly nicer way so at the beginning of each loop what i can do is call mock reset on the create user function and this will just reset everything back to its initial state so that each individual loop will be a brand new test on that function that's really what i want i want to make sure that each of these tests is individual and independent from the previous ones so now if i run this test that's all passing because the username and password is correctly being passed to my database create user function and on the topic of this mock reset i actually like to create a before each block and reset this function before every single test just to make sure because there is state that will be kept track of between individual tests and we want each test to be able to run individually and independently of each other it's just nice to reset that state back to its initial default state before any single test runs when the server calls the create user method here and it passes in a username and password this should return the id of the user that was just created in the database and what i want to do is check that the server actually grabs that return value and sends it back down to the client in the response body so let's see what that test is going to look like so before i actually make the request to the server before i make that post request i'm going to tell the create user function that it should resolve to the value one and instead of just marking a return value i know that this is going to be an asynchronous method because in my real database this has to be asynchronous as to make the database connection so i'm marking the resolved value which means that it's going to return a promise that resolves to the value one uh then i make the request and then i'm checking that whatever value was passed in here in this case one is in fact whatever the user id is in the body of the http response so if i run this test this should fail but again this isn't a great test because i could just come in here and change this value to 1 and then that would make the test pass so instead of just testing one single user id value what i'm going to do is put it in a for loop again [Music] so now i've just set up a basic for loop i just chose 10 randomly and every single time this loops through it's going to increment the number by one so the first time it tests this it's going to mark the result value to zero and then it's going to test that it actually got zero back in the user id and uh just to be safe i'm also going to mark reset this on every single loop so before this happens we reset it we mark the result value uh make the post request and then check that whatever id came back from the database because this is the database function uh is sent back in the response to the client so if i run this test now this should hopefully fail perfect because it was expecting zero it received one cause i've hardcoded one in there so now in here the way of making this pass is gonna be pretty simple so now we're just going to get the return value back from the create user function and send that back to the client because that is the user id so if i run this test now so now all my tests are passing and what we've done here is tested that the interaction with the create user function is working the way we expect it to work so in my app file this function has been tested using a mark checking that the interaction works and the idea is that now if i pass in a function that connects to a real database we know that it's going to call it correctly with a username and password and whatever return value we return out of this function it is going to use correctly so as long as i test this function independently and then i just inject this database into the app.js file then everything should now be working and we get to test things independently so that's it for an introduction to marking and using just mock functions in a future video i'll actually go over how to test a database so that we can test the other part of our app
Info
Channel: Sam Meech-Ward
Views: 13,506
Rating: undefined out of 5
Keywords: javascript testing, unit tests, tdd, rest api, jest testing javascript, jest testing, jest mock function tutorial, mock function jest, javascript testing tutorial, javascript testing with jest, mock async function jest, mock function jest react, jest testing node js
Id: IDjF6-s1hGk
Channel Id: undefined
Length: 13min 29sec (809 seconds)
Published: Tue Apr 06 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.