How To Write Tests For React Container Components

React Testing Library's approach to integration testing like a user would interact with the DOM works very well with React's function-based components.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the The newline Guide to Modernizing an Enterprise React App course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to The newline Guide to Modernizing an Enterprise React App, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course The newline Guide to Modernizing an Enterprise React App
  • [00:00 - 00:11] Now that we've gotten our app ready to run integration tests, it's time to write some integration tests. Fair warning, this is going to be the longest lesson in this module, so get ready.

    [00:12 - 00:27] If you can stick with it to the end, I think that you'll feel a lot more confident about how to test these components on your own. In this lesson, I intend to walk you through writing tests for a couple of our container components, relying on React hooks.

    [00:28 - 00:34] It's time to get to work on these tests. Since the app.js file already has a single test, let's start there.

    [00:35 - 00:57] We're going to completely overhaul how this file looks because this course is not only about modernizing enterprise React apps, it's also about showing you best practices for bigger code bases, and that includes how we structure our tests. The first thing I prefer to do when writing test files is to set up a describe block around each set of functionality within a test file.

    [00:58 - 01:12] If it helps, you can think of describe as a wrapper in the test suite to divide up functionality. Then each test inside of that describe block will be a specific test for one particular piece of functionality encompassed by the describe.

    [01:13 - 01:34] A good example of when described comes in handy might be for a file like our product list.js that has the ability to both display our products and also filter which products are displayed. These two things are pretty different, so I might choose to break up those two pieces of functionality displaying and filtering into two separate describe blocks within my test file.

    [01:35 - 01:50] Our app.js file doesn't have that much functionality though, but consistency within a code base is also important, so to stay with best practices we'll give it a describe anyway. If we look at the app test now, it's basic.

    [01:51 - 01:58] Let's go ahead and wrap this one test's functionality within a describe. Nothing fancy, just a bare bones description of the file's purpose.

    [01:59 - 02:13] So if I was going to write this, I might do something like this. Describe, hardware handler app, and then an arrow function, and then our test inside of that.

    [02:14 - 02:31] The next thing that we're going to do is purely optional, but it's more familiar to me and the way that I've been writing software and tests, so I'm going to do it even if just to show you how it's done. We're going to change our test syntax that our first test began with and switch it to use it instead.

    [02:32 - 02:47] It and test are pretty interchangeable according to the just documentation, but I prefer the syntax of it, which looks like it should do something. If you would prefer test, did not do another thing, that's fine, you do you.

    [02:48 - 02:55] Or whatever your team has already established at their preference, just stick with that. Remember consistency is important.

    [02:56 - 03:15] But since I'm writing this course, this is what I choose to use. So our original test string is going to go from test renders hardware handler without crashing to it should render without crashing, render the app without crashing.

    [03:16 - 03:25] You might be wondering when should you use describe versus when should you use it in tests. Describe is best for breaking your test suite into smaller groups of tests.

    [03:26 - 03:40] Depending on your test strategy, you might have a describe for each function in your class, each module of your plugin, or each user facing piece of functionality. You can also nest describes inside of one another to further subdivide your test suite.

    [03:41 - 03:47] I rarely do this myself, but it is possible. It is for when we're writing individual tests.

    [03:48 - 04:01] We should be able to write each test like a little sentence, such as it should show a product price when the item is rendered, that sort of thing. Now it's time to beef up this test because it is a little skimpy on the details .

    [04:02 - 04:12] When we look at the home page of hardware handler, we see a lot more than just the welcome message. We see links, we see buttons, and we see a checkout count of items if items are present.

    [04:13 - 04:24] Let's put these into the test to make sure that they are visible when the app starts up. As you can see from the beginning of this test, we declare a local variable called title that is simply an element of text on the page.

    [04:25 - 04:41] This is not required for the test to work, but it does make it easier to understand the element in the DOM that we're targeting, especially if that element will come into play in more than one test assertion. Just like title, I'll declare a few new variables for the other elements that I 'm looking for on the page.

    [04:42 - 04:54] The links to the product page, the add a new product page, and the checkout page. Underneath the title variable in the test, we're going to add the following variables.

    [04:55 - 05:15] Next, product button, which is screen get all by text, my products. Const new product button, which is screen get all by text.

    [05:16 - 05:30] Add new products. And finally, const checkout button, which is screen get all by text, checkout.

    [05:31 - 05:44] These three variables will now search the rendered page for these pieces of text. Note that surrounding the particular strings you're looking for, like some text on screen with I, makes the text search case insensitive.

    [05:45 - 05:55] It will find all combinations of upper and lower case versions of that text. If you want it to be case sensitive, just wrap the text in quotes with whatever capitalization is expected.

    [05:56 - 06:09] After defining these variables, we'll need to search for their presence on the page. These are a little different than our title variable, though, because each text string is on the page twice, once in the nav bar and once on the main page.

    [06:10 - 06:23] Instead of searching for each text string to be in the document, we'll need to check that each text string has a length of two indicating it's visible twice in the rendered components. So here is the syntax that we can use to check for that.

    [06:24 - 06:43] We are going to expect product button dot to have length of two. And then I am going to copy that and switch this out to be new product button and checkout product button.

    [06:44 - 06:48] Okay. So let's run our tests and see what happens.

    [06:49 - 07:04] In your terminal, cd into the client and then run yarn test. Okay, I had a failing test, so let's figure out what happened.

    [07:05 - 07:13] Get all by test ID. Get all by text.

    [07:14 - 07:18] So let's try that again. Yarn test.

    [07:19 - 07:27] Nice. Our first tests pass, meaning all the text that we expect to see is rendered when the app loads.

    [07:28 - 07:34] So if you look at the code coverage for this app test js file currently, it's decent. Let's go run it.

    [07:35 - 07:49] So kill that and then yarn coverage. From the command line, we will do open coverage lcove report index dot html.

    [07:50 - 07:59] And if we switch over to our browser and we look at the app dot js file, it's decent. We have an overall line coverage rating of almost 78%.

    [08:00 - 08:15] But as a rule of thumb, I want my integration test overall code coverage to be 80% or above to feel confident in my code. Looking at the coverage report in the browser, we can see the two functions bringing down our coverage percentage are the use effect and the update checkout count.

    [08:16 - 08:23] We should be able to cover that use effect function that displays a number next to the checkout link in the nap bar. Let's write a test for it.

    [08:24 - 08:32] Switch back to your IDE. And underneath our original test, create a new it statement to check if there is a count present in the browser.

    [08:33 - 08:50] So we will write an it and say it should display checkout count in the nap bar when there are items in the checkout. And we'll make this a new arrow function.

    [08:51 - 09:02] Then we'll write our test in here. So in order to display a count in the nap bar, we're going to need to mock our use checkout custom hook because that's what supplies any item counts to this component.

    [09:03 - 09:08] We can do that. First, we're going to need to import the use checkout hook into this test component.

    [09:09 - 09:26] And we're going to do so as a wild card. So at the top of the file, import star as use checkout from hooks slash use checkout.

    [09:27 - 09:43] Then we're going to use just spy on function to spy on the actual function that we're calling, which is also named use checkout inside of our hook inside of our newly written test. This is why I imported the hook using the wild card syntax instead of the named import.

    [09:44 - 09:59] So inside of our test right down here, we're going to declare a new const, which we will call mock use checkout. And we are going to do just dot spy on use checkout.

    [10:00 - 10:05] And then the name of the function, which is also use checkout. Little bit confusing.

    [10:06 - 10:29] For mocks, I tend to use the name of the function that I'm mocking and just stick the word mock in front of it to indicate that whenever this function is called by the code being tested, this mock will take over that function and return the data that will define. So if this hook was supplying essential data to make our component render, we would need to declare this spy and this mocked data in a before each block before all of our tests.

    [10:30 - 10:39] But since the app won't crash without any of that data, it just won't show a number next to the checkout icon. We'll just add the mock for the single test that really needs it in this file.

    [10:40 - 11:10] So if we look at the real data that the use checkout hook returns, which we can open right here, we see that it gives us back an object of destructured properties, a checkout count, a checkout items array of objects, a set checkout items function and an error boolean. You'll need to mock all these values within an item for this test to have the data that it needs with no items in the checkout, no numbers rendered and the test isn't doing its job.

    [11:11 - 11:21] With our use checkout function already being spied upon, we can now define what it should return when the mock is called. Here's the test data that I will tell our mock took to return.

    [11:22 - 11:37] This is also going to be inside of our second test right after the declaration of the mock use checkout variable. So right here, we will say mock use checkout dot mock return value.

    [11:38 - 11:49] And then in here, we will have a checkout count of one checkout items and an empty array. And then in here, we will have our object.

    [11:50 - 12:35] We'll do a brand of test brand, a department ID of one, a description, because my test description, an ID of one, a name, a test name, product ID, a lot of IDs, a quantity, one, and finally a retail price, which we'll just put one, two, three, four. And then after that, we will set our checkout items as a jest dot fn, jest function, and we will set error to false.

    [12:36 - 12:48] Okay, good. So when our app component gets rendered now in the test that we're about to write, it should have all the data it needs to think that there is a checkout item and display account in the nav bar for it.

    [12:49 - 13:07] Because we're mocking our use checkout hook, which provides the state of our app component, we don't have to pass any props or set any other state before we can render this component in the test. And then after rendering it, we'll look in the DOM for the number one, which is what should be present now, provided our mock took is working, and that should be it .

    [13:08 - 13:18] So here is going to be our new test code after we have returned our mock. We will render our app once more.

    [13:19 - 13:39] And then we will create a const, which we will call checkout count, and that will be screen yet by text, and we will search for one. And we will expect checkout count dot to be in the document.

    [13:40 - 13:59] Okay, so at this point, if you're not already running your tests using the just CLI in watch mode, which tries to rerun them after every change, let's go ahead and do that now, and then we will check our new code coverage again. So run yarn test right here.

    [14:00 - 14:12] And it looks like we have one that failed because I have a typo in checkout button, which should be get all by text. Not sure how that happened.

    [14:13 - 14:17] But we'll rerun it again. And it looks like both of them passed.

    [14:18 - 14:28] And so we will cd into the client in this terminal and run yarn coverage. And then we will check what our test coverage looks like.

    [14:29 - 14:36] So if we switch back over to the browser, refresh the page. We are now at 89% code coverage.

    [14:37 - 14:47] The only bit of code still not covered now is when the update checkout count function is called, but I think it will be easier to test that that works in another file. So let's move on and test another component now.

    [14:48 - 14:58] Go ahead and switch back to your IDE. That was a good warm up file to get our feet wet testing react components using hooks, but it's time to try out a component with some serious functionality in it.

    [14:59 - 15:12] I'm thinking the product list component would be a good one because of its ability to display and add products to the checkout as well as its filtering capabilities. So let's go ahead and start setting up for this test.

    [15:13 - 15:28] Just like how the app component had a test folder inside of its app folder in our project, we're going to do the same for product list. So in your IDE, open up your product list folder and create a new folder inside of it called test.

    [15:29 - 15:38] Inside of this folder, create a new file named productlist.test.js. Okay, we're ready to start writing our tests.

    [15:39 - 15:55] With our file created, it's time to set up our first describe block for our tests and then we'll figure out what our actual product list component needs in the way of data for it to render. So as with app.js, we'll start off with the most basic describe and its statements here.

    [15:56 - 16:15] So we will describe product list component, make that into an arrow function and inside of that we will have it should render the component without crashing. And once more, make an arrow function.

    [16:16 - 16:25] Okay, so let's look at product list itself. Let's open it up and close down that so we have a little bit more room.

    [16:26 - 16:35] So when we examine the component that we're testing, I see that it requires info from two of our custom hooks. The used department's hook and the used product's hook.

    [16:36 - 16:43] If either of those hooks data is missing, the component will throw errors. So we'll need to set the mocks for these hooks up before every test.

    [16:44 - 16:53] Test just the thing for this, it's called before each. So let's mock our used department hooks first in our test file because it will be the easier of the two.

    [16:54 - 17:08] So at the top of our file import the used department's hook, use the wild card import so we can target the used department's function within our mock. And right inside of our describe block, we're going to create a mock used department's variable that spies on the hook.

    [17:09 - 17:35] So we will import star as used departments from relative path hooks use departments. And then inside of our test, we're going to have a mock use departments, which will be a jest dot spy on and then used departments.

    [17:36 - 17:41] And then the function name, which is also used departments. Oh, and I forgot the equals sign.

    [17:42 - 17:43] There we go. Okay.

    [17:44 - 17:54] So after the variables declared, we can mock the data that we want to be returned. And since we need this data available for every test, we'll set up the data being returned in a jest before each method.

    [17:55 - 18:02] The used department's hook returns a destructured object containing both an array of departments and an error Boolean. So our mock should do the same.

    [18:03 - 18:07] It's going to be crystal clear that it's mocked data. We're not going to use real data.

    [18:08 - 18:20] I'm going to create some departments that don't exist in our real app to prevent any confusion. And just so you know, I put this in the wrong place, move it outside of your it so that all of our tests can reach it.

    [18:21 - 18:33] So we will create a before each function. And inside of it, we will call mock used departments and mock return value.

    [18:34 - 18:48] And the departments array that I am going to make is going to have two departments. We're going to give one the ID of 45 and a name of garden tools.

    [18:49 - 18:58] And the other one is going to be the second object. We'll give it an ID of 56 and a name of appliances.

    [18:59 - 19:04] And then finally, we will add one error down here and we will say false. And that should do it.

    [19:05 - 19:09] That'll work for our used department's hook. Now we just need to mock our used product hook.

    [19:10 - 19:17] This hook has a bit more to it. It returns an array of products and array of filters by brand and an error Boolean.

    [19:18 - 19:27] So we are going to import this hook just the same way that we did for used departments. This will just be used products.

    [19:28 - 19:40] And we will switch that to the correct used products import. And then the very same way that we did this spy on for mock used departments, we will have mock used products.

    [19:41 - 19:51] And we will spy on the used products hook and look for the used products function. Then we are going to add mocked data to return.

    [19:52 - 20:08] I would recommend that you copy the data out of the lesson because there is a lot of it. Do take note though of how the products department IDs align with the faux department IDs that we set up in the first mock.

    [20:09 - 20:19] So add this mock right under our first one still inside of the before each and then close the before each and we are done. So I'm going to copy this out of our lesson because it's easier.

    [20:20 - 20:27] Copy that and then right after this mock ends, paste that all in. Great.

    [20:28 - 20:38] So in addition to our data setup before each test runs, we also need these m ocks to reset after each test runs. So we need to use the just after each method to reset our mock states.

    [20:39 - 20:48] So right after our before each, we are going to add our new after each. And that is going to look like this after each.

    [20:49 - 20:54] And then we are going to just run just reset all mocks. That's all we have to do.

    [20:55 - 21:05] As the name implies, the method resets the state of all of our mocks before the next test runs. That's got a ton of handy functionality like this that makes integration testing easier.

    [21:06 - 21:16] Here's a tip for mocking data. Oftentimes when I need to mock data that's complex, like the data coming back from our used product took, I like to let the actual apps functionality help me out.

    [21:17 - 21:34] Inside of the product list file, I added a console log right after the used product took was called with all the values that it returns to make sure that I was including exactly the right values in my mock. No sense trying to remember it or work harder when I can just view what I already know works in the browser.

    [21:35 - 21:46] Now that our two mocks are set, we can go on for testing. Inside of the first it, where we check that our component loads, we'll render the page and check that some key pieces of info are in there, like the page title and filters.

    [21:47 - 21:59] In future tests, we'll get to the details like filtering and products. Similar to the tests with our app.js, we'll rely on React testing library's get by text method to find these elements in the component.

    [22:00 - 22:10] So here is what our first test code should resemble to ensure that it's loaded. We'll need to import a few items from the React testing library into this file before we can write our tests.

    [22:11 - 22:23] For now, I think that render and screen should do the trick. So we will import render and screen from at testing library react.

    [22:24 - 22:36] And then we're going to write some code that checks that various things get rendered. So the first thing that we're going to need to do is render product list, which has yet to be imported into this file I might add.

    [22:37 - 22:42] So go ahead and do that if you haven't already. Auto import saves the day.

    [22:43 - 22:52] And then from here, we are going to make a page title, which we will do screen. getbytext.

    [22:53 - 23:18] And my products case insensitive, const get filter, which will be screen.getby text filter by department. And finally, const brand filter, which is screen getbytext filter by brand.

    [23:19 - 23:30] And once we have those declared, we can do some expect assertions. So we're going to expect the page title dot to be in the document.

    [23:31 - 23:41] And then I'm going to duplicate that and switch out page title to depth filter and brand filter. Cool.

    [23:42 - 23:48] And if we see our tests running, now we have three passing. So now we're going to go more in depth on testing the component.

    [23:49 - 23:54] In my mind, there are two distinct parts that need testing. First, the list of products that is displayed.

    [23:55 - 24:01] And one of those products can be successfully added to the checkout. And second, the filters by brand name and department display work.

    [24:02 - 24:12] And we can narrow down the list of products displayed when checked. So we're going to make two more describe blocks inside of our original describe , right under the first test, one for products and one for filters.

    [24:13 - 24:13] Sounds good? OK.

    [24:14 - 24:24] Here is my first describe block that focuses on the list of products in particular. Describe products on the page.

    [24:25 - 24:35] And we will make that into one describe. And then the second one will be describe product filters.

    [24:36 - 24:37] And that will be our second. Easy enough.

    [24:38 - 24:46] So let's add some product specific tests. So as before, a good burst test is to check that when data is supplied from our mock hooks, the list of products renders in the browser.

    [24:47 - 25:00] Since each of our four products being rendered has a unique name, we can't necessarily check that something like the word product shows up on screen four times. But we can check that the word description, which is included with each product , does.

    [25:01 - 25:10] So that's how we'll count the number of items that should be visible. We could also throw in a couple of assertions to make sure all the product info that we should be seeing on screen shows up.

    [25:11 - 25:29] Feel free to pull out whatever little data details that you like. So inside of our first describe, we are going to write an it statement and say it should render products when the use products hook hook returns data.

    [25:30 - 25:36] We will make this into an arrow function. And we will render product list.

    [25:37 - 25:44] We have to render the component. And once we've done that, we will create a const that we'll call products.

    [25:45 - 25:53] And we will do screen get all by text. And we will look for the word description.

    [25:54 - 26:02] And once we have that, we can make some expectations. We will say products dot to have length of four.

    [26:03 - 26:11] We can also do the expectations of screen dot get by text. And I'm just going to look for a couple of product details.

    [26:12 - 26:21] So I know that one of my products has that black as part of its description as its color. So we're going to look for that.

    [26:22 - 26:32] And we're going to expect that to be in the document. Likewise, I know that there is a rose sun hat that is one of our products.

    [26:33 - 26:44] So I will use get by text and search for rose sun hat. And expect that to be in the document.

    [26:45 - 26:57] And finally, I will expect screen get by text. And we will look for the price of $9.99 to be in the document.

    [26:58 - 27:13] And that gives us a pretty good confidence that all of our intended product data is there when our hook gives us product info. And if we look up right now, we will see that we have an error for matte black because I forgot to add the eye.

    [27:14 - 27:20] So if we add that and let our tests rerun, now all of them are passing. Great.

    [27:21 - 27:39] And since we're testing when the custom hook returns data, we should also test if the hook returns an error to make sure that our app correctly handles that scenario as well. For this test, we'll need to override the mocks that run before each test in our before each handler and pass in some data for an error state for one of our mocks.

    [27:40 - 27:53] This can be accomplished by simply redefining the mock inside of our test where we want an error to be thrown before we call the render method on the product list component. We'll also check that our error message displays when this error is present.

    [27:54 - 28:12] So here is the second test that we're going to write inside of this describe. We will say it and should show an error message when there is a problem fetch ing data from either book.

    [28:13 - 28:22] And we are going to create a new mock use departments dot mock return value once. That's the difference.

    [28:23 - 28:38] And we will say for this one that error is true and departments is empty. Here I now once again we will go ahead and render our product list component.

    [28:39 - 28:53] And we will say const error message is equal to a weight and we will do screen dot find by text. That's what we have to do with an await and we will look for dollar sign.

    [28:54 - 29:16] fetch department data error fetch department data error and then please refresh the page or try again later. And we're going to expect this error message dot to be in the document.

    [29:17 - 29:27] So if you notice this test has an async awaits syntax which I forgot to add the async right up here. So go ahead and add that now.

    [29:28 - 29:41] Little bit long to see all at once. So this async await because it takes a second to return the error state after the component first renders and the react testing library find by text syntax is async.

    [29:42 - 29:54] We also have to import the fetch department data error constant in our file here. Also take note that we're using just mock return value once because we only need this particular mock to run once and then never again.

    [29:55 - 30:03] This is a way to chain together successive mock calls with different values as well if that's required. Pretty straightforward so far right?

    [30:04 - 30:16] Inside of the lesson I have included a quick link to the react testing library cheat sheet so you can know which queries are async and which queries are available to you. This chart still helps me out to this day.

    [30:17 - 30:20] Okay. Let's write the last test for the products portion of our testing.

    [30:21 - 30:36] We're adding a product to the checkout. To make this test work we're going to need to add an extra mocked API call for the add item to checkout function and we're also going to bring in a library that we haven't used up to this point the user event library.

    [30:37 - 30:48] It was installed when we upgraded our app to use the latest version of create react app but this is the first time that we'll need its functionality. You may be wondering why is there a separate DOM interaction library.

    [30:49 - 30:58] If you're familiar at all with react testing library you may already know that it provides built in functionality to interact with the DOM via methods like fire event. And that's true.

    [30:59 - 31:17] But user event is a companion library for testing library that provides more advanced simulation of browsers interactions. In addition to more advanced methods the syntax for user event lets us write more concise code than we could write using fire event and I'm always for cleaner syntax.

    [31:18 - 31:29] There are some limitations to the extent of user events current capabilities but the library is constantly improving. So what once was an issue might no longer be one the next time that you reach for it.

    [31:30 - 31:40] Down to the business of our test. As I said we'll need to import both the user event library and the checkout API at the top of our test file so let's go ahead and bring those in.

    [31:41 - 32:03] First we're going to import user event from testing library user event. And next we're going to import star as checkout API from and then a relative path to our services and our checkout API.

    [32:04 - 32:15] With these available we're ready to set up our final test and our mock for the checkout API's add items to checkout function and its response. So this is how I'm going to start our test out.

    [32:16 - 32:35] Here is the last file that we're going to add. So we'll say it should successfully add a product to the checkout when the add to checkout button is clicked.

    [32:36 - 32:43] This too is going to be an asynchronous test. So go ahead and add a sync right here and that will set us up.

    [32:44 - 33:17] So the first thing that we need to do is const mock add item to checkout and to do that we will use just dot spy on and then the checkout API and the add item to checkout function. And then mock add item to checkout mock return value and we're going to do the product added to checkout success and put a little space in between that to make it easier to read.

    [33:18 - 33:32] Now we're ready to render our component and add a product. Since all our products have the same add to checkout button on them we're going to need to use the same get all by text function and then have our test click the first element that it finds with that text.

    [33:33 - 34:02] So we will start out with render product list and then we will have a const that we will name add button, screen get all by text add to checkout and then we will use zero to just get the first element in the array. And finally we are going to wait and then we will do user event dot click and we will call add button.

    [34:03 - 34:15] Finally we're going to check that our mocked add item to checkout function was called with the product data that we set up at the beginning of the test file. And I am going to copy this in because this expect is quite long.

    [34:16 - 34:24] So go ahead and copy it from the lesson and then we'll talk about it. So I'm going to paste that in and save.

    [34:25 - 34:41] And unfortunately this is about the only thing that we can do in this component to verify that the item was added. I've worked with things like react to testify before and due to the fact that it only lasts on screen for a certain period of time it's not the most reliable way to ensure something has happened.

    [34:42 - 34:50] This is why I'd recommend checking the function was called with the expected data instead in this sort of situation. And that should cover us for product testing.

    [34:51 - 35:01] If you like you can run the tests and code coverage now and then we'll move on to our second describe block for testing filters. Okay, we're now testing the product filtering for product lists.

    [35:02 - 35:10] So stay with me. As with our other initial tests let's check first that when our custom hooks provide data the filter options are displayed.

    [35:11 - 35:20] You should feel pretty confident in writing this test. Under our component check that a few filter options for each type of filter are present done next test.

    [35:21 - 35:39] So inside of our product filters describe let's go ahead and write our first test. We will say it should render filter options when the use products hook and use departments hook return data.

    [35:40 - 35:56] Make it an arrow function. And then inside we will render our product list component and we will do an await screen dot find by text and we will look for appliances.

    [35:57 - 36:19] To know that it is rendered and since I did an await I need an async up here at the beginning of the test. Okay, and then we will create some filters and we will do document dot get elements by class name and we're going to look for the class name of filter item.

    [36:20 - 36:47] Then we will do filter department count which is going to be document dot get elements by class name and we're going to look for filter data. We're going to take the first array and then we're going to make a second one that is filter brand count which will use the same document get elements by class name.

    [36:48 - 37:00] We're going to once again use filter data but we're going to get the second element in that array. And it's not actually the elements it's their children.

    [37:01 - 37:06] I'll go into detail about this soon. Hang with me for now.

    [37:07 - 37:38] So children, children and then after that we can do an expect and we will expect filters to have a length of five and we can expect filter department count to have a length of two and filter brand count to have a length of three. Excellent.

    [37:39 - 37:56] What you might notice in the test above is the extra line waiting for the text appliances to appear on screen. When I first wrote the test I wasn't waiting at all after the component rendered and both mocked hooks didn't have a chance to return the data that is expected to render all the filter hooks.

    [37:57 - 38:07] Originally only three filter options were visible by the time the assertion started to run but there should have been five. Appliances however is one of the two departments that should be a filter option .

    [38:08 - 38:21] So we can be fairly confident that when the department option is visible all of our filter options should be loaded and we can successfully check for all of them. Another thing to take note of is how we're accessing the various filters variables.

    [38:22 - 38:43] React testing library doesn't offer a way to access elements on the page via classes or IDs because that's not a way that a user would normally be able to access them either. And while we could add something like data dash test ID to the elements we need to access which React testing library considers as a sort of last resort escape hatch option we won't.

    [38:44 - 39:09] Instead we're going to use documents.getElement by class name which is built straight into the browser's document model. I'm showing you this because if you end up in a situation where you can't add a data test ID to an element say because you're using a component library like ant design where component details are hidden from us while writing the code this is what you'll have to do to interact with certain elements in tests.

    [39:10 - 39:12] Make sense? Okay let's keep going.

    [39:13 - 39:26] Let's try and figure out what I did to make this test fail. If we scroll back up we see that our test our newest test failed.

    [39:27 - 39:33] Let's see what it tells us. We're able to find an element with the text of appliances.

    [39:34 - 39:43] That would be because I continuously forget to add this I. If we rerun that all of our tests now pass.

    [39:44 - 40:01] So now that we've written our test with good data let's write a test when our hooks have an issue fetching data. Just like with our test in the products test block we'll override our test suite's successful mock hooks with failing mocks to ensure that the error message is displayed for the filter section too.

    [40:02 - 40:18] So we are going to write an it that will say it should render error messages when either hook returns errors. This too will be an async function.

    [40:19 - 40:43] Okay we're going to make mock use departments fail again so we will have mock return value once. We will have error true and the departments will be an empty array and we will also make mock use products fail with mock return value once.

    [40:44 - 41:42] It will be error of true filters by brand of an empty array and products of an empty array and now that we've got that set up we will render our product list component and we will expect an await screen dot find by text and we're going to look for the text and load department filters and await screen dot find by text and load product brand filters and I'm going to save that correctly and save. I hope that you feel a little more comfortable writing this once you've seen us writing our other tests.

    [41:43 - 41:53] We have one more test to go. The very last thing that we're going to test is if the filters correctly narrow down the list of visible products when only particular filters are selected.

    [41:54 - 42:09] So for this test we should check that the count of products displayed on the page is before filtering. We're going to select a check box make sure the product count is lower and then unselect that same option once more to ensure that all the products return to view.

    [42:10 - 42:29] So here is how I would approach this test. I would write one more it and it would be it should filter products displayed on page when filters are checked and unchecked.

    [42:30 - 42:46] And because we're going to be interacting with this it will be an asynchronous function. We are going to go ahead and render our product list component as usual and then we're going to find our check boxes.

    [42:47 - 43:14] So we will use the screen get all by roll and we're going to look for the check box roll and then we're going to expect a weight screen dot find all by text. We're going to look for description once more and expect it to have a length of four.

    [43:15 - 43:35] Then we're going to call a user event. The user event dot click will go with the first check box in the array and then we can expect a weight screen dot find all by text.

    [43:36 - 43:49] Once again we'll use description and we're going to expect it to have a length of two. And finally we will do basically the same thing.

    [43:50 - 44:00] We will unclick that same check box and we will expect the length to go back to four. So some cool methods that React Testing Library offers are the by roll functions.

    [44:01 - 44:15] Methods like get by roll make it easier for us to select elements like buttons, inputs, check boxes, etc. on a page. So I just target the elements on this component with a check box roll, select and deselect the first check box elements and test the filtering.

    [44:16 - 44:21] Not too bad. Alright it's time to run all of our tests and our code coverage again.

    [44:22 - 44:31] Let's see where we're at. Go ahead and run yarn coverage and if you've been following along you should see something like this in your terminal.

    [44:32 - 44:40] Now if we look at the product list we are up to 92.73%. I'm very satisfied with that result.

    [44:41 - 44:49] At this point you may want to take a break. This has been a long lesson and we're close to getting done but we're not quite there.

    [44:50 - 45:02] Before we move on there are a few details that I want to clean up a bit in regards to this test. The first thing that I'd like to do is move our test data that we mocked at the top of this test suite into a separate file.

    [45:03 - 45:17] I want to do this for two main reasons. One to clean up our test file and make the code easier to read and understand for future devs and two to make this data reusable so that other test files that might need it won't have to copy paste the data there as well.

    [45:18 - 45:38] So to accomplish this let's create a new folder at the root of our source named MOCS data or something similar so it's easy to identify the purpose of this folder and its contents. So I will move my terminal down and out of the way and close up a couple of our folders so that we can see our source folder again.

    [45:39 - 45:49] And right here we're going to create a new folder that we're going to name double underscore MOCS data double underscore. Okay.

    [45:50 - 46:00] Inside of this folder we're going to create a new file called MOC data set dot JSON. This is where our test data is going to live.

    [46:01 - 46:17] So we're going to take all of the data that we're setting up in the before each function in productlist dot JS and we're going to add it to our JSON file. When the file is done it should resemble something along the lines of this which I'm going to cut out.

    [46:18 - 46:33] So I am going to scroll up to product list, scroll up forever. I am going to take all the values inside of this mock use departments and paste that in and save that.

    [46:34 - 46:57] Thank you VS code. And additionally I am going to take all of the values from products right here, grab all of those, copy that and right after this error false add a new comma and remove this fun because we already have an error false and save.

    [46:58 - 47:00] Here we go. So that is all of our JSON data.

    [47:01 - 47:16] We now have departments and error and our products and some filters by brand. Within the product list component let's import our new JSON data at the top of our file because we no longer need to have any of it here.

    [47:17 - 47:31] So we are going to import something that we will name data set and this will be from MOCs data, mock data set dot JSON. And then we can replace all of the hard coded data in our MOCs.

    [47:32 - 48:16] So instead of all of this we are going to replace that with departments which we will set equal to data set dot departments and error which will be data set dot error. And likewise in products we will delete all of this and replace it with products which will be data set dot products, filters by brand which will be data set dot filters by brand and finally error which once again will be data set dot error.

    [48:17 - 48:34] And since we have done this we can also replace our hard coded values in the test where we are checking the add item to checkout API that happened. So right down here where we are doing mock add item to checkout expect we can actually replace this entire thing with one of our data set products.

    [48:35 - 48:57] So we will remove that, remove this extra curly brace and instead we will just say data set products and then the first object in the array much cleaner, much better, much more reusable. I will admit this way of doing it slightly abstracts the data but I think that the benefits here outweigh the drawbacks.

    [48:58 - 49:17] Okay, one last issue to address because when you are running the test as they are now you may notice something odd in the console. We have this weird error message that says an update to product list inside a test was not wrapped in act.

    [49:18 - 49:33] For reasons that even I am not completely clear about the test that adds a product to the checkout is not satisfied with our using the user event library to click the add to checkout button to add an item. Although the test passes I'd like my terminal not to be upset about this.

    [49:34 - 49:48] So I fix the error by switching this action from using the user event library to using the traditional fire event action that's built into the main react testing library. Using this also requires importing act from the react dom test utils library.

    [49:49 - 50:02] So let's go fix our code. We are going to bring in fire event and we are also going to bring in act from our other library.

    [50:03 - 50:25] This is going to be react dom test utils and down here in our test where it should successfully add a product to the checkout. Close the rest of these for easier readability.

    [50:26 - 51:02] So should successfully add a product to the checkout we are going to change this user event to be an await act async and then inside of here an arrow function and we are going to do an await fire event dot click add button. And if we watch our tests again everything seems to be working better except react dom test utils cannot be found which means either I didn't import it or I messed something up.

    [51:03 - 51:13] So import act from react dom slash test utils. Okay.

    [51:14 - 51:26] And then when we go back over here. Now we see that all of our tests are passing and everything is good in the terminal.

    [51:27 - 51:36] This simple code change fix the error and with that resolved I feel good about this component and the functionality that's being tested. Here's my rule of thumb for integration testing.

    [51:37 - 51:56] As I mentioned earlier I like to aim for code coverage that's 80% or above for the project as a whole. In my mind that's enough to give me plenty of confidence that I've tested our mission critical paths within an application but it still allows some wiggle room so that I'm not a slave to having 100% coverage of those really hard to reach edge cases.

    [51:57 - 52:14] And when I'm looking at the generated code coverage report I focused mostly on the first metric that's printed out that's the overall percentage of code tested by file and for the total project. The rest of our container components that you'll be integration testing should be relatively simple and similar to what we've just worked on.

    [52:15 - 52:28] If you'd like to take a stab at testing some of our other components as a challenge to check and solidify what you've just learned over the course of this lesson, please be my guest. In our next lesson we'll tackle testing custom hooks with React Testing Library .

    [52:29 - 52:31] They're a little bit different than what we've covered up to this point.