Testing a Next.js App with React Testing Library & Jest

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today we'll take a deeper dive into testing a react or next.js app as we look at unit tests versus integration tests with jest and the react testing Library [Music] hello and welcome I'm Dave today we will learn about testing an application with unit and integration tests using jest and the react testing library and I'll provide links to all resources in the description below I'll also provide a link for you to join my Discord server where you can discuss web development with other students and you can ask questions that I can answer and receive help from other viewers too I look forward to seeing you there in the previous videos of this testing series we've set up jest and the react testing Library confirmed it was all working and we've written a few simple unit tests if you haven't watched those videos I recommend you start there and I will link to them in the description below specifically you want to go through the next JS testing video that helps you set everything up because I will be using that today but I won't be going back through the setup again in this video the overall goal of today's lesson is to dive deeper into writing tests with the react testing library and talk about the difference between unit and integration tests and while we won't cover everything that's possible today I think I can give some good examples to help you begin testing your applications and these examples should help you understand the documentation better as well I'm providing the starter code that you see here for this lesson you can download the repository from GitHub at the link in the description and ensure you have node.js stalled and then when you open up the project in vs code open a terminal in the root folder and type npm install and this will install all of the dependencies you need to run the project and run the tests and it will be the same ones that I list here in my package Json specifically all of these Dev dependencies and I want to point out this dependency in particular the just Dom here for the testing library because I'm not using the latest version the latest version currently seems to have an issue with typescript types and so I have rolled back to 5.16.5 and then the types are all correct and hopefully that gets corrected in the future so you may be watching this video and you won't have a problem with the latest version of this dependency however right now I think you'll want this one because typescript will give you errors without one that of course recognizes the types the application we'll be testing today is a simple to do app and I don't think you need me to review an entire to do app so we're not really concerned about writing the code for the app today we're more concerned about looking at how the tests are structured for each component and then how the tests are structured when we test all of the components together let's cover the types of tests and you can see here I Googled what is a unit test and what comes up right away is an answer that says it's the smallest piece of code that can be logically isolated in a system and we're going to test that so isolated is important as we come back and write our unit tests for each component so that is how we're looking at unit tests we're isolating the component and then we're testing it without any outside dependencies if we have those we're going to learn how to create mock dependencies so we can just test that component in isolation so what about integration tests or integration tests versus unit tests well I Googled that as well and here's a good description it says unit tests always take results from a single unit such as a function call but integration tests May aggregate results from various parts and sources and there's no need to mock away parts of the application like we would with a unit test and I was talking about that of course we'll get into that however so the integration test think about components working together in our to-do list we have a component where we can add a to do and then of course we see that new to do in the to-do list which is another component so we're going to write an integration test to check how all of that works together now one other type of test I wanted to bring up that you may heard of is e to e or end to end and I actually think of this as B beginning to end because as they say here a testing method that evaluates the entire application flow from start to finish so if you think about this as a user of a website you would go there possibly create a login and then log into the website add some things to a cart go through the checkout process and then maybe log out that's complete end-to-end testing and we don't do that with unit tests and integration tests where we look at each piece or how they work together so that's something we would do possibly in the future in another tutorial with Cypress or Puppeteer Now quickly before we start looking at code I want to go to the react testing library site or the testing library.com site actually because it's available for more than just react however on this site we're looking in the react section and at about queries and now I want to highlight the different types of tests we'll be writing when we write these unit tests first and there is a get buy and they've got h chart here that says okay if nothing matches it will throw an error if we get one match it's going to return the element if there's more than one match it's once again going to throw an error and it's not async and this is get by which is a very common type of test now if I'm looking for something that I think won't be on the page I'm going to choose query by because that returns null instead of throwing an error if there are zero matches and so that's typically when I reach for query by other than that it's the same as above Now find by very much like get by except it's async so we'll keep that in mind as well now multiple elements we also have get all by query all by and find all buy so they really are the same except these methods are going to turn return arrays and so we'll keep that in mind as well as we look at the code I'm back in vs code let's look at each individual component in our application and you can see I've got the add to do folder open here let's also talk about the organization a little bit and actually I'll do that first so see we have a test directory with two underscores in front and two underscores Behind These directories will be fined automatically when we run our tests and we can put tests inside some choose to keep this outside of the source directory in a next project and then just put all of their tests in that one directory however you can organize it with the components and that's what I've done here so this directory that says tests is for the component that we have here in the page.tsx and we'll come back to it but right now we want to look at each of these individual components notice that the nav bar is the only one I didn't create a separate folder for with my organization here and that's because I didn't write any tests for it it really just pulls in the header component and I'm testing that separately we could write a test to check to make sure it renders a nav element if we want to and if you want to do that that would be good practice I'll leave that to to you after we look at the test for the header component here is our header component and you can see it receives a title as a prop and then it's going to render that title in a header so let's look at the test now notice there is another test directory inside of this header directory and when we look at this we could write a couple of tests to make sure it renders what we expect so here we have it and you could use this as test as well either would work so some examples you may see test written and some you may see it I like it because it reads as a full sentence here it should render the next to Do's heading and notice now we're rendering the header component and I'm passing in the title value of next to Do's then we actually get that on the page and as in the previous tutorial I've talked about the triple a pattern here so we start by arranging and that's when we render the actual component and then we act and here we're going to get that component where we have screen get by roll we're searching for a heading and now we can specify here in the options the name as it should be the heading that displays next to do's and we expect that header then to be in the document and that's the third a that's the assertion or assert so we have arrange act and then assert and then I essentially run the same test again with another value I put my name in there and we make sure that it works in the same way so we didn't hard code in that value next to Do's for example so that's a very simple component with a couple of simple unit tests here and that's what this is It's testing in isolation notice we surrounded it with the describe that just said a header and we were just checking a couple of things as they would render on the page so now let's move on to the next one and let's start at the bottom with the to do items so as we look at our test for the to do item or we could look at the do item component as well it's really fairly simple so I've got an article parent here that should should be returned and rendered then we have a label that has the to do title in it and then after that we have a div surrounding the input which is a check box and that would toggle it as if it was complete or not either true or false there and then we have a button that should delete the to do from the list as well so that is our delete button so now if we go to the test let's look at how this is organized and there's several things to note in this file so notice we're importing render and screen as I did in the previous tutorial we wrote A Few simple unit tests in that tutorial I'm also bringing in the user event now this kind of simulates a user it can actually trigger more than one event so if you've watched other tutorials that have fire event they're only triggering that one event but actually using the user event is more like what you would see from a user now no this is not the same as end-to-end testing but at the same time it can uncover some some things that might be unexpected otherwise so using user event is a good idea and then we're just bringing in that to do item component of course now notice I've got a mock to do here because we're testing this in isolation and then I need a mock set to Do's as well because this component receives both the to do and set to Do's that we see so this mock set to Do's you can use jest dot FN which stands for function and that can just be a mock function we're not really looking for how the set to Do's works but we can confirm that it is called so now let's look at our tests here I'm going to scroll up and I'm also going to press Ctrl B to hide the file tree so we just have a little bit more room to see the code notice I've got to describe inside of a describe here so first I'm describing the component as add to do but then I've organized my tests within so I have a render described where I'm checking how things should render and then as I scroll little further down I've got another describe on behavior and this is how I expect some of the things in the component to behave so let's quickly look at these renders they're all very similar tests so we should render an article so we get that by roll just looking for an article element and we expect it to be in the document and then we also have render a label notice this is different because get by roll will search for the actual element we can't do that as easily with the label so I have get by test ID when you have a hard time finding something in a different way you can apply a test ID and I want to show you that in the component as well so I'll bring the file tree back over here to the to do item notice I've got data Dash test ID and then I set that to delete button on the button that we have inside of our component here so that's how we are finding that with our test okay back to the test now we see we looked for well actually this was the label with to do item I did that for the button as well let me come back and highlight another test ID here we go so control Z or no that is alt Z actually I got that to wrap down so it wouldn't extend off the page the data Dash test ID equals to do item on the label that's the one I needed to find here at least at first and that's how we're finding that on the page so I'm using that get by test ID and it's to do item and then after that we're just expecting it to be in the document and the third small test for a render here is checking the check box and we can get that by roll each to do item just renders one check box and we expect it to be in the document finally should render a button and here is the one that I was talking about before but I actually in this instance I use get by roll and I'm just looking for the one button later on when we look at the list and there's more than one button on the page I'm going to need that test ID so very simple render test and that's all in the describe block that's inside of the parent describe block we have up here for the component so now let's look at the behavior tests and I'll also hide the file tree once again now the first one it says should call set to Do's when a check box or when checkbox clicked so notice we're passing in our mock set to do's and we can test for that so we find that check box then we await the user event and we click the check box and then we can expect the mock set to-do's function to be called and because we set that above with our jest function we can check for that and then we do the same thing down here should call set to Do's when the button is clicked because that will also happen when we delete a to do we'll filter out the one we're deleting and of course set the to Do's again so very similar test but instead of finding the check box or finding the button and then the user is clicking the button with our user event here and we're checking that to be called and once again this is all tested in isolation now I'm going to to bring the tree back over and after the to do item we could check the add to do or the to-do list let's go ahead and look at the to-do list first and the tests inside I think the to-do list is what you would probably expect in a to do and or a to-do application and here you can see if there are no to Do's we have no to-do's available otherwise it's going to render the to-do's and we're sorting them in reverse order here so then when we map over sorted to Do's we get the most recent one at the top that way and I could press alt Z again to wrap that down just so you can see here's our to do item so in a way they're working together already when we're testing this but I am not testing anything about the to do item we're going to focus on this to-do list so once again the to-do list receives the to do's and set to Do's so now I have an array of to-do's instead of an individual to do and this is mock to do's and then once again I have the mock set to Do's here now I'm describing the to-do list let me hide that tree over there and now it says should render no to-do's available when the array is empty notice here I'm not using the mock to Do's I'm just passing in an empty array because that's what we want to check in this specific test and then I'm passing in mock set to Do's as it expects now we should get by text and find no to-do's available that should be on the page and we expect that message to be in the document the next test should render a list with the correct number of items and so now we're going to render the to-do list and we're going to pass in the mock to Do's as well as the mock set to do's and then we're going to get all by roll this is the first time you've seen that remember this returns an array it's the get all right here and so now we have an array and we have the mock to-do's list set with just the two to Do's in it so we should expect that to Do's array length to B2 and that's our assertion now let's go to the last one it should render the to Do's in the correct order so we're making sure the new to do is on top essentially and we can check that by knowing something about our mock to Do's above see I'm looking for the get coffee here and that is the second to do it has the ID of 2 but when it's rendered it should be the first one so when we run this test and we render our to-do list the first item we can use get all by test ID and we're getting the to do item and then we're getting the first element in that array so the first item should have the text content and here it reads as expect to have text content and we get coffee and of course we have the coffee emojis as well now let's go back to the file tree once more and look at our last component that is going to focus on the unit test here and this is our add to do and as you might expect it's a simple form and we have a handle submit function that is going to call set to Do's inside of it so here we're returning this and let me hide that again so we can see more but our return in our jsx is the form right here it has a hidden label and we don't really test for that but we could and probably should in the renders but I didn't think of that before but of course if you don't have a label here you'll get a warning about not having a label with an input it's not really critical to the function though we're hiding it as well so other than that we have our input right here that's a text input and we have our button right here that is the submit button for the form itself so now let's go back and look at the tests or this and of course hide that file tree so we can see better and once again there are enough tests I am breaking it into blocks so after the describe add to do and notice this only receives the set to Do's it doesn't receive the actual to do's and that's because anytime you have set to Do's if you don't need the to Do's by themselves if you only need them in the setter you can reference that in that function that can pull up the previous to Do's already and that's just something about react that's not specific to jest or react testing library or even next JS that's just part of react okay so inside of our add to do describe then we have the render and I scroll down a little further and we have Behavior again so let's quickly look at what should be rendered after we render our component we should be able to get that input the text input by looking for the placeholder text that it has which is a new to do and we expect it to be in the document we should also render a disabled submit button it's important to note the disabled part of that so that's what it should be in or its own the button State when it's first rendered and after we render the component we get that button and then we can expect the button to be disabled now this is kind of a two in one because we know it rendered the button and we know it's disabled scrolling down let's look at our Behavior tests so we should be able to add text to the input and after we render our component once again now in our act we have a couple of things here and then the assertion after so we've got the input we get it through that placeholder text once again then we use a user event so here we are awaiting once again and we have user event DOT type we're going to type into that input that we've found and I'll just type hey there so we expect the input to have value hey there now in the next test it should enable the submit button when text is input and I said enabled let me just say should enable the submit button and so when text is added to that input the submit button should then be enabled and once again we grab that once again we type into the input and then we get the button and I'm using get by roll and then I'm looking for the button that has the name submit it's displaying submit on the button and then we expect that button to be enabled after that our next test said should empty the text input when submitted so we go through these same steps again but now we're going to click the button to submit the form and then we expect the input to have a value that is an empty string instead and finally because mock set to Do's is passed into our component when it's rendered here then we can go through everything once again and click the button and we can expect mocked set to Do's to be called so I'll save my simple change and now of course I'll go back to the tree now I just want to highlight if I'm going too fast for anyone that's because I've provided all the code I didn't want to type out each test and have you go along that way it's kind of boring to do honestly not a whole lot of people like just writing tests however it's important that you learn about them so I'm more or less providing the code AS examples that will hopefully help you write tests for your applications in the future okay now that we've looked at each individual component let's look at the integration testing that we have for our main page here and notice here in our main page there's a couple of things I really need to highlight one is I've just set this to use client so it's essentially a react app as much as an xjs app because everything's in the client I made this a very simple to-do app and when you set the parent component to use client then you don't really need to do that for the child components because if the parent is a client component then all of its children will be and as a matter of fact typescript is going to complain if you go ahead and set something like your to-do list under here as a use client component no I don't notice I don't have that here at the top and if I did my app would still work but I would get an error in the dev console at least as things stand right now and it would say something about serializable props not being passed in and that's because notice it's receiving set to do use so it knows right away that the parent has to be a client because you can only use a hook like use state in a client component and if this is receiving a prop of set to Do's then it's parent has to be a client as well but even if that wasn't the case any child of a parent component that is a client component is going to also be a client component so just highlighting that as well okay back to the page.tsx and now notice here I didn't do an API call I am just providing a few to Do's to start out with and that's because in the next tutorial I create we're going to mock an API and look at how to add that probably to this application because that's the part here that's really kind of not real life we've just started out with a few to Do's you could add more of course or delete these but normally you're going to have an API call and some crud operations right create read update and delete we'll look at that next time around but in this page we're just providing some to-do's to start out and this is going to be an integration test because what we're doing here is really just pulling in the add to do component that we unit tested already and pulling in the to-do list and of course it pulls in the to do item component as well so we're going to make sure all of those components work together as we look at the tests for our page.tsx which I called home.test.tsx because the component in the page.tsx if we look up here at the top is named home of course next.js has a page.tsx at every level as we would go through and that's not what we want so much if we had if other routing here for application we would have a lot of page.tsx files so you want to name them what you have named the function so we'll go with home.test.tsx okay let's look look at this now because this is much like what we were doing before but we're just making sure everything works together and remember the integration testing said okay now you don't need to mock away set to Do's or mock away the actual to-do list and that's true but at the same time you could mock an API like I'll be looking at in a future video now that said we're not passing anything into our home function either we just render home and so that's why we've got some to Do's already being created here in the page.tsx that we can work with as well so now let me go back to the home test again and the first one is it just should add a new to do simple enough right we've done much of this before we grab the input we let the user type we await that user event DOT type and then we expect that value my new to do to be in the input so that's the first step and then we act on that and we get the button and then we await the user event where it clicks on the button then we expect the input to have an empty value because it should empty out afterwards as well finally we get the data here because we await the screen and now notice I'm not using a git buy I'm using a find buy and if you remember from that chart we looked at for react testing Library find by is async so notice before we never had an await before the screen but now we do and now we can look for that minute to do because it would take just a little bit we have to await that to actually be on the page right and then we just take I name this data but we expect our data to have text content that says my new to do I'm going to scroll up once again hide that file tree just so we can see this next test better this should update it to do so we once again render the component now we need the check box and and here since we're using typescript and working with the checkbox can be a little tricky notice I used an assertion here in typescript where I had get all by roll because at this point now there's a check box on the screen for every to-do item that's in our list and so we're getting that very first element and then I'm using the typescript assertion as HTML input element and that will allow typescript to be okay with you checking the checkbox dot checked value and I'm checking it here at first because it should be false at first and if we look at look back at our component once again I come down to the page.tsx notice they're all false when we start out here with this data so that's one thing we could test there maybe in the future that would be a little bit more difficult to test but since we know what data we have we're checking that and we expect it to be false at first and then we're changing it and here we're just awaiting that user event clicking the check box is check box checked now truthy instead of falsy and finally let's see if it deletes a to-do so once again very similar but not quite the same because now we're getting the button one notice once again that I have get all by test ID and I'm looking for that delete button ID now and that's what I talked about earlier in the video where they all have the test ID delete button because there's more than one button on the page and we also have a button to add a new to do on the page and we don't want to select that in this array either so we just look for the delete button ones then I get the first one in the array and I await the user event where it clicks the button and then I'm using the to do text and notice I have dot not to be in the document so we at this point we don't expect it to be in the document so above I used query by text instead and that's what because if we search for that at some point and it's not in the document we could have an error now here at the beginning it should be there and query by text works much like get by text but anytime I'm looking for something not to be in the document I kind of reach for query by text when I'm writing my tests so there's the full integration test for our application where we checked all of the components together and notice that I'm really using the same tools from react testing library that I did to write the individual unit tests where we tested each component in isolation now I know there's much more to testing but this will really help you get started looking at a very simple application like this and next time around I want to mock the API calls and check out those crud functions as well now a couple of things I also want to note here about next JS especially the latest version of next js13 with the app router if you're looking at testing a component that has used router that's fairly difficult right now and we have to mock the next slash navigation I may create either a blog post or a video on that in the future and then server actions are still uh you know they're still experimental either alpha or maybe they've moved up to Beta I think they're still Alpha and so if you try to test a server action right now that's not going to work out either but overall this helps you test the basics of components and next time we'll be looking at mocking those API calls remember to keep striving for Progress over Perfection and a little progress every day will go a very long way please give this video a like if it's helped you and thank you for watching and subscribing you're helping my channel grow have a great day and let's write more code together very soon
Info
Channel: Dave Gray
Views: 16,056
Rating: undefined out of 5
Keywords: testing a next.js app, next.js, nextjs, react testing library, jest, react, reactjs, react.js, next.js unit tests, react unit tests, next.js integration tests, react integration tests, unit tests, integration tests, unit testing, integration testing, unit vs integration testing, unit tests vs integration tests, unit testing vs integration testing, next.js jest, react jest, react testing library next.js, writing unit tests, writing integration tests, testing a react app, testing, js
Id: XTNqyEBPAFw
Channel Id: undefined
Length: 30min 29sec (1829 seconds)
Published: Fri Sep 01 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.