Testing the Home Component II
In this lesson, we complete our tests for the Home component.
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo 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 TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two with a single-time purchase.

[00:00 - 00:16] In the last lesson, we prepared our very first test for the home component, and now we'll move a little bit ahead and try to create the other tests that we can think of for this component. We can think of doing at least two more tests for this search input.
[00:17 - 00:39] Now, if we go back to the home component, the way we had the search input work was if the user provided valid information, and for us, valid information was just not simply in blank string, we will take them to the listings page and append whatever they searched for. If they simply searched an empty string or an empty value, we'll display an error message.
[00:40 - 01:09] So, two other tests we can think of doing are a test around the fact that it redirects the user to the listings page when a valid search is provided. And the other one is the opposite of this.
[01:10 - 01:27] We can just simply say it does not redirect the user to the listings page when an invalid search is provided. When it comes to how we want to set up this test, it's going to be practically identical to the test above, right?
[01:28 - 01:43] We just simply want to have a home component rendered, and we don't necessarily care about the mock graph quilt request because the search input is shown, regardless of how that request fares in the home page. So that being said, I'll just simply copy this from the above.
[01:44 - 01:59] And I'll also take the wait for utility being used, and I'll use it in the test that we're creating as well. Since we need to actually do something with the same search input, we'll probably need to find the node as well.
[02:00 - 02:16] So I'll also go ahead and specify that we're looking for the search input node, and we'll use the same query utility that we used before. We'll try to find the node by the placeholder text of that specific node, which is search San Francisco.
[02:17 - 02:30] Now this is where in this particular test, we're going to actually invoke the change we want for this search input. There's two things we need to do for this search input for the user to be navig ated to the actual listings page.
[02:31 - 02:52] The first is the actually need to change the value of the input. The second thing they need to do is they need to confirm their change in our UI that was either by clicking the button that was adjacent to the input, like the actual search icon, or the actually pressing the enter key.
[02:53 - 03:05] By doing so, if the search is valid, they'll be navigated to the listings page. Now when it comes to helping fire events, this is where we can utilize the fire events utility from the React testing library.
[03:06 - 03:24] So we'll import this and be prepared to use it. With the fire events utility, we're able to trigger practically any event that we can think of in our UI, change events, key down events, key up events, click events , etc.
[03:25 - 03:42] Since we're dealing with an input form here, when it comes to actually changing the value of this input, we can look to trigger the change events. This change event often occurs when a value of an element has been changed, and this only works for input, text area, and select elements.
[03:43 - 03:51] These convenience methods from the fire event helper take two arguments. The first is the element in which we actually want to trigger the event.
[03:52 - 04:04] And the second is the options of the event being triggered. So the very first argument is pretty evident which node we're trying to have the event triggered for, and that is the search input node.
[04:05 - 04:16] In the second argument, this is where we specify the properties or the options of the event being triggered. And this is where we specify the actual properties of the event relevant to the event itself.
[04:17 - 04:32] Since we're doing a change event here, this is where we can specify a target and a value of the change that we want. Assume in this particular test, we wanted to navigate the user to Toronto.
[04:33 - 04:45] And it doesn't really matter what value used here, but we'll use a relevant value such that we can actually test that this would work when we trigger the other event that gets followed. Let's quickly reiterate what we're doing here.
[04:46 - 04:56] We're using the fire event utility from React testing library to help trigger a change event in the input. This allows us to mimic the behavior that the user is actually typing something in.
[04:57 - 05:04] We're specifying the change event since we want to change the value of the input. The first argument, we declare the node that we want the event triggered.
[05:05 - 05:12] And in the second argument, we're specifying the options of the event change. And this is where we say the value is going to be Toronto.
[05:13 - 05:21] Now to have the second event be triggered, there's two ways we can have as mentioned before. The event to be triggered such that the user is submitting the inputs.
[05:22 - 05:31] They can either click the button, adjacent to the input. And for us to do that, we'll find that node and then trigger a button click event, or they can just simply press the enter key after the input has been made.
[05:32 - 05:37] So we'll go with that approach, just having the enter key be pressed. To do so, we'll use a different event.
[05:38 - 05:44] And in this particular case, what we can do is we can use the key down event. Similarly, we want to trigger this event on the same input.
[05:45 - 05:53] So we'll provide that as the first argument. And now here for this key down event, there's two values we can provide.
[05:54 - 05:59] You can provide the key in which we actually want to trigger. And we've mentioned you want to trigger the enter key.
[06:00 - 06:05] Or we can provide the key code as well. The key code for the enter key is 13.
[06:06 - 06:17] And this is something I just Googled and you can always Google around to ensure that this is the right number. I also do believe we can specify the key code of just enter as well, but we'll go with 13 and we'll see if that works.
[06:18 - 06:22] So what are we doing here again? Very similar to the above, but this time we're triggering another event.
[06:23 - 06:29] We're trying to trigger the fact the user presses down on the enter key. We have the note as the first argument of this event.
[06:30 - 06:36] And in the second argument, we're specifying the properties of the event that we want triggered. We're not saying the user has clicked the up key or the down key or the left key.
[06:37 - 06:46] We're saying the user is trying to click the enter key. Okay, we're now have the act of our test be occurring.
[06:47 - 06:51] We can now just try to come up with our assertion, right? And we can try to assert where we're looking for.
[06:52 - 07:02] What is our assertion in this instance? In this test, we want to verify that when the user does what we think they're doing, they type the right information, they press enter, they're redirected to the appropriate location.
[07:03 - 07:15] So in this instance, we just simply want to assert that the location the user is in is where we expect them to be. And to do this, we can actually take advantage of using the history object that now exists from the context of router that we provided in our test.
[07:16 - 07:26] So we can say we expect the history location path name. And this is where we can use the 2B helper method.
[07:27 - 07:35] I think we can use either 2B or 2Equal. I shouldn't matter in this instance, but we just want to say that the path name is going to be what we think is going to be.
[07:36 - 07:41] So what is the path name going to be in this instance? The path name is slash listings, right?
[07:42 - 07:45] That's what happens in our components. The user is navigated to slash listings.
[07:46 - 07:54] And then what's appended is the value that they searched for. So technically, in this particular instance, we should see listings slash to run.
[07:55 - 07:59] So let's save our changes. This is pretty much the test we want to do.
[08:00 - 08:05] Unless assert that it works the way we expect it to. We'll go back to our terminal.
[08:06 - 08:09] And it sees that the second test is passed. We don't have anything in the third test.
[08:10 - 08:14] So it's just blank at this moment so it doesn't do anything. But it tells us that our second test is passing.
[08:15 - 08:25] If we want to verify that this isn't just a dummy test that's somehow working, let's provide listings and show some other information here, such as listings and why . Just random value.
[08:26 - 08:35] And this is verified and this would fail if we don't put the actual value we expect. And we see a failure happen.
[08:36 - 08:45] There's some logs about the actual UI that's being logged here by the very top. It tells us that what's expected is listings and why and it receives listings Toronto.
[08:46 - 08:56] So things are working the way we want to and our test passes. Our other test, the one below that it does not redirect the user is going to be practically identical, right?
[08:57 - 09:05] Let's just see how similar it's going to be. We'll copy everything we have in this above test here and bring it down to our third test in the bottom.
[09:06 - 09:16] But in this instance, we want to verify that it does not redirect the user under the condition that they don't provide valid information. And for us, valid information is just a blank string or an empty value.
[09:17 - 09:22] So we'll specify that the event for the search input has nothing's been input ted. It's just an empty value.
[09:23 - 09:31] If the user tries to press the inter key, they won't be navigated anywhere. And by default, the history location path name is always in the index path.
[09:32 - 09:43] So it's to be just slash. So here we're saying if the user provides an invalid value such that there is nothing provided and they press enter, they're not going to the listings page.
[09:44 - 09:50] Let's save our changes and let's see how this would work. Amazing.
[09:51 - 09:55] And our third test passes. That's pretty much exactly what we want to do for the search inputs.
[09:56 - 10:04] We managed to validate the initial value, even though this was a very straightforward test. Other two tests were more, I guess, relevant to the functionality we set up.
[10:05 - 10:12] If the user tries to provide a valid information, they're taken to the listings page. If the user provides an invalid information, they're not taken anywhere.
[10:13 - 10:17] And this is pretty much all the tests will write for the search input. There could be other tests as well.
[10:18 - 10:27] Like for example, in the home page, we know that we display an error message if it's invalid. So for example, you could perhaps say if it's not redirected, an error message is also shown.
[10:28 - 10:30] That could be a separate test. We can group this in this test.
[10:31 - 10:35] There's no right or wrong here. But I think in my opinion, this is the relevant testing that I wanted to do.
[10:36 - 10:44] It's important to know that the user isn't taken to that page when there's not valid information and we've managed to validate that. So we're pretty much good for now.
[10:45 - 10:55] We'll now move over to writing tests describing how the premium listings in the home page is going to be shown. So now when it comes to premium listings, we quickly mentioned how we're thinking of testing this.
[10:56 - 11:07] But let's reiterate this again. The premium listings section in the home page is essentially where we show the highest price listings and this information is given to us from our graph to our query.
[11:08 - 11:19] When this query is loading, we just simply show a child component to be created that just simply shows cards in their skeleton state, right? And this is to visually tell the user that, hey, this is loading and we're trying to get information.
[11:20 - 11:25] When this information is available, we show those cards where we actually feed them with information. So we render our listing cards.
[11:26 - 11:36] We get the data for each listing, we fill it in, boom, boom, boom. If neither happens, which basically is not loading and no data exists, this probably means in mind of just error, we show nothing, right?
[11:37 - 11:54] Now in other testing libraries that I've used before, what I would often tend to do here is I would actually check for the presence of this child component in the parent. So literally in my test, I will say something like when the query is loading, the home listings skeleton component is shown.
[11:55 - 11:57] The data is available. The whole listing component is shown.
[11:58 - 12:08] That's how I used to often do this before. And with my other testing libraries, whether it's an at work where we have our own custom library or some other ones I've used before, this was given us the ability for us to do.
[12:09 - 12:16] However, React testing library moves us away from this. And with the React testing library, they tell us to not focus on implementation detail.
[12:17 - 12:23] That makes sense in a way, right? Because the user is unaware or doesn't care about the fact that this component is labeled or names listing skeleton, right?
[12:24 - 12:32] The user is only interested in seeing relevant UI information. So with the React testing library, we're encouraged to actually find information in the UI.
[12:33 - 12:51] The very first number one query helper that we should often think about with React testing library is getting by role. So it's often encouraged for us to find particular nodes from the fact that a certain element exists, whether it's a button, a span, whatever it might be, right?
[12:52 - 13:04] Now between these two states, whether it's loading or not loading, the roles between them are pretty similar, right? So what we'll try to do instead is we'll try to look for the fact that a certain title is shown, right?
[13:05 - 13:13] So technically, when data is available, the user should be presented with a title of premium listings. Like this should appear in the UI.
[13:14 - 13:25] Similarly, we can try to make this happen for the skeleton just for the case of our test. So we'll say home listing skeleton here would now have a title prop that says premium listings loading.
[13:26 - 13:49] And in the home listing skeleton component, we'll quickly just say it expects a title prop. And we'll see the implementation in the other home listings components, where we simply just used the title component from topography from ant design.
[13:50 - 14:12] So we'll take that in import typography, get the title sub component from typ ography. And in this instance, we'll simply just place the title instead of the skeleton line over here, we'll just show a title that says it's loading, it removes the skeleton import.
[14:13 - 14:18] Save this, save this. Again, to reiterate why we're doing this, this is what we're going to use for our test.
[14:19 - 14:34] For our test, we're simply going to see if the actual title information or just simply the text of loading or not loading is shown depending on the use case. Depending on how you architect your UI, you're more than welcome to do as you please here.
[14:35 - 15:02] But I would just encourage you to think about the hierarchy of opportunities or the hierarchy of helpers that we should stick with from React testing library, React testing library encourages us to use things that are accessible to the user. So first of all, get by role to find things based by role, get by label, or label text, I believe might have been might have been misnaming it, but this is relevant for four fields where there's an input and there's a label gets by text get by text is one of the higher things.
[15:03 - 15:07] And that's what we're going to use in this instance. This will make sense once we just quickly write our tests.
[15:08 - 15:10] And they're going to be pretty straightforward. They're not going to be difficult.
[15:11 - 15:25] So we can sort of prepare the test we want to write here, right? So the very beginning, we want to specify that it renders the loading states when the query is loading.
[15:26 - 15:33] That's the first test you want to specify. You want to ensure that some loading information is shown when our GraphQL request is still in the loading state.
[15:34 - 16:02] The second thing we want to specify is it renders the intended UI when query is successful, right? And the last test you can think about here is it renders nothing when query is or has an error.
[16:03 - 16:09] We'll just say that for now. When it comes to the loading state of the query, we'll set this up.
[16:10 - 16:14] We'll set this test up very similar to how we set up the test before. So we'll copy this above.
[16:15 - 16:25] We'll talk about it in a second and we'll use the wait for utility because this will be relevant or very relevant in this instance. Cool.
[16:26 - 16:37] Now we don't necessarily care about history information as always, but we're using it because our component uses React Router's route to link component. So we needed to specify the router context for our tests.
[16:38 - 16:47] So we're still doing that regardless. The mock provider component here is now relevant because this is what's going to help us control the status of our request, right?
[16:48 - 17:02] We can now mock the behavior of whether our request is loading, whether our request is complete, whether it has an error. To specify that the request is in the loading states, we just need to continue to display an empty array in terms of this mock's property.
[17:03 - 17:09] This is how the Apollo testing library for React expects it to be. We did the same above, even though it wasn't really relevant.
[17:10 - 17:19] So technically in the test above all these requests were loading, but the search input wasn't, you know, dependent on this. However, in this particular test, it's dependent on the fact that our request is loading.
[17:20 - 17:27] And lastly, we need to now wait or make sure anything that's happening as ynchronously is done. And we're using the wait for utility.
[17:28 - 17:31] We're saying a wait here. So in this instance, we'll say async await.
[17:32 - 17:42] Note that again, for the loading state is still in the very beginning. We don't necessarily have to wait for the loading state to be there because the request happens right away and it's loading right from the very beginning.
[17:43 - 17:49] However, we're still using this. We noticed some console warnings tell us that there might be some state changes and whatnot.
[17:50 - 17:57] And since we're doing some asynchronous requests to work anyway, we're still going to use this utility as much as we can. Let's now try to come up with our assertions.
[17:58 - 18:15] We'll now try to look for the nodes that we want and verify whether they're there or not there. The way we can try to approach this is we can use the query by helper because in this instance, this query by helper will necessarily error if they can't find a node.
[18:16 - 18:23] It will either return the node when found or it would return no. And this will help us conditionally determine which of the titles is being shown.
[18:24 - 18:36] And we can say something like we expect this premium listings loading title to be not to know in other words to be there. However, we also expect the premium listing title to not be there.
[18:37 - 18:40] And this tells us that we will be in the loading state. So how can this look?
[18:41 - 18:49] We'll specify the expect clause and we'll say something like query by text. What is the text we're looking for here?
[18:50 - 19:09] We're going to look for the loading text, premium listings loading. And we're going to say we expect query by text, premium listings loading to not be no.
[19:10 - 19:17] I always get this mixed up. I think the more appropriate one is we can use the not as the additional thing to be no.
[19:18 - 19:24] There we go. We'll see a particular helper method and just allows us to specify not to verify that it's not what we want it to be.
[19:25 - 19:32] So in this case, we're saying we expect the fact that this node is not to be no . So this node is to exist.
[19:33 - 19:42] And very similarly, the opposite of this or the other particular node is just premium listings. We expect this node to be no.
[19:43 - 19:49] Why are we using the query by variant here? Because this either returns no or not to no when something can't be found.
[19:50 - 19:57] And this is the basis of how we're trying to write these tests. We're trying to expect the validation of certain thing is found and a certain thing is not found.
[19:58 - 20:18] The node with the title that has loading should be there, not be no, or the node that doesn't have loading should be no, should not be there. Whether you want to use something else as opposed to no to be defined or to be undefined or the fact you want to specify a different way of doing this instead of using query by text, you're trying to say get by text.
[20:19 - 20:27] There's many ways to go about doing this. But I think at the end of the day, this wouldn't help us validate to the extent that we want that the loading states will actually be shown.
[20:28 - 20:42] So if we save our changes now, head over back to our terminal. While our tests pass, those last two tests here are just empty tests.
[20:43 - 20:56] So this doesn't matter that they pass, but the test here that says the loading state is what we want is passing. So this tells us that what we try to do works in the test below where we want to ensure the intended UI is going to be rendered.
[20:57 - 21:04] Our assertions are pretty much going to be the opposite, right? So for example, we can literally just copy everything here for now because we 're going to use pretty much almost all of it.
[21:05 - 21:17] We'll make this asynchronous as well because we're going to wait for any changes to be done. And now instead, we're going to simply say, we expect this not to be null and we expect this to be null, right?
[21:18 - 21:24] This makes sense. If you look back at the home components, we'll see that if data is available, we render a separate component with a different title.
[21:25 - 21:34] If it's loading, you render the skeleton components with the loading title, right? So now once this data is available and everything is good, this should not be null and this should be null.
[21:35 - 21:47] What's the main difference we're going to do in this test to now have this assertion actually work? We're going to provide mock data in our mock provider that tells us that data is now existing.
[21:48 - 22:01] So we can just simply create just an object up here called listings mock, perhaps. And now we can try to prepare this mock in such a way that we can tell our test that, hey, this request is successful, assert that the UI works when it is.
[22:02 - 22:13] So this is now where we have to prepare the mock in such a way that directly resembles the query we make in our home component. And in this particular query, we're making a query for the listings graph Q on query.
[22:14 - 22:25] And we're using some of the variables here where some of these variables are just numbers, but one of these variables is a value from an enum. So what we can do is first we'll try to import the listings query itself.
[22:26 - 22:37] We'll go back to our tests. And we'll import the listings query in particular.
[22:38 - 22:52] The second thing we'll import is the listings filter, enum. And now we'll try to construct this listings mock object.
[22:53 - 23:00] And here's where we'll first specify the request being made in the mock. This is how the payload of the mock object is expected to be constructed.
[23:01 - 23:09] Very first thing is the query itself, what query is being made its listings. Second thing is what are the variables we've specified in this query.
[23:10 - 23:13] Right? So we can go back to this home component and we can see that these are the specific variables.
[23:14 - 23:31] So what we can just do is copy this directly, paste it in, page limits instead of having a constant we just say for this instance and page number will be one. And now with this request sort of specified in our mock object, we'll declare the results.
[23:32 - 23:38] This again is just a format that the Apollo testing library expects. This instance, we want it to be successful.
[23:39 - 23:47] So in the results, we'll say that we expect the data to come back the way we wanted to come back. The object that gets returned from data is listings.
[23:48 - 23:58] This is the GraphQL object that actually gets returned from our query. And listings contains a few fields, region, total and result.
[23:59 - 24:03] Now this is just mock data. So technically the information we put here isn't really relevant, right?
[24:04 - 24:16] Because in the test specifically, we're not trying to verify if the information matches the data, we just want to verify that we see the right UI when data is available. So we'll just put some dummy information here for region.
[24:17 - 24:21] We'll just say no. For a total, we'll just say 10.
[24:22 - 24:39] For result, which is to be an array, we'll just put a single object. A specific listing object has the following fields from our query, an ID, a title, an image, an address, a price and number of guests.
[24:40 - 25:07] We'll go ahead and fill in some dummy information for all of these fields. The one other thing that the Apollo testing utility documentation tells us is to consider the use of an ad type name prop.
[25:08 - 25:20] So with Apollo client, Apollo client usually adds a underscore, underscore type name field to every object type requested. And Apollo client uses this for caching and normalizing storing responses.
[25:21 - 25:38] However, in our mock, when we are using and importing the raw query, we're importing it without type names from the file that we imported from. And by not having type names, in this particular instance, you may see issues where the imported query won't match the query actually being run.
[25:39 - 25:48] So to help avoid any issues, what we're going to do is just specify add type name equal false. And in this case, we're basically saying that our queries are lacking type name .
[25:49 - 26:03] So we're just specifying and telling that to the mocked provider. And lastly, the only thing left to do is now in this mock's prop, we just need to specify the usage of this listings mock.
[26:04 - 26:08] Using our changes saved when we head over to the terminal. Amazing.
[26:09 - 26:17] The test we just created renders the intended UI with the query as successful as passed successfully. So our test currently passes.
[26:18 - 26:34] But here's an interesting thing. If I scroll up to where we're specifying the request of our mock GraphQL request, and if I was to simply just change some numbers here, so let's say limits instead of four, I say one, right?
[26:35 - 26:45] And now if I was to go back to my test running in the terminal, the tests fail. This is something that I actually hit a snag with for a decent amount of time.
[26:46 - 27:06] And that drove me a little off the edges because I was just so confused as to what was going on. So something that I've come to understand and I'm still doing more reading on this is the fact that when we specify our mock request here, our mock providers asking us to specify it in such a way that it matches exactly what we're doing here, right?
[27:07 - 27:16] It's not necessarily just a mock response. It's a mock response, a request, sorry, that matches exactly the request being set up in your components.
[27:17 - 27:29] And that's how it sort of conditionally makes the match and then tells the test specifically that this is the response that's going to come back. So as a result, if I change this back to four, for example, we go back to my tests.
[27:30 - 27:37] This works. So again, this I think is understood from my part now because I spent a decent amount of time trying to figure out why something wasn't working.
[27:38 - 27:48] But if you're hitting any issues, if this is what I did to debug, it might be helpful actually. So if you're ever hitting any issues in a test, I'm going to go in a bit of a tangent and a bit of a unrelated discussion.
[27:49 - 27:51] But I think it's pretty helpful. So you see that this fails, right?
[27:52 - 28:02] And unfortunately, this test tells us that what we expected to not be no was no , it said it received no. But fortunately, other than this, we don't get much logs and much information.
[28:03 - 28:23] We do get a, I guess, a display of what the actual UI is, but it's a bit hard to read what's happening here. Something that's incredibly helpful that I tend to do when it comes to tests is when a test is failing and you're unsure what's happening, first of all, you know, you can actually specify only a single test is going to run with a just by using the only keyword.
[28:24 - 28:31] So if you say it, only, if you go back to our logs, our terminal, it will show that only that test is running. Cool.
[28:32 - 28:34] So this is helpful. Why is it super helpful?
[28:35 - 28:47] Because one, we're not, you know, bothered by any other tests, but two, since this is only running specifically, we can now actually go to our components and we can try to inspect what's happening. So if you ever feel like I'm not sure what's going on, I think I did everything correct.
[28:48 - 28:55] Why is this not working? I can literally just do a console log here and be like, okay, I know this function is running.
[28:56 - 29:02] What is the status of loading? And similarly, what is the status of data?
[29:03 - 29:08] Like this data exists. Is something happening is, is it not showing is data not being available?
[29:09 - 29:21] And if I save this now, if I was to go back to my, my terminal, it will tell me what's sort of happening, right? So at the very beginning, late loading is true.
[29:22 - 29:25] Why is it automatically true in the very beginning? Because we're making an asynchronous request, right?
[29:26 - 29:32] So the minute, um, a query is being fired with Apollo, the first thing they do is they set loading to true and data is undefined. So this makes sense.
[29:33 - 29:43] However, right after loading becomes false and that makes sense as well, because we're saying we've said it in our test, we want to wait for until our actual request is complete. That's fine.
[29:44 - 29:56] However, data is still undefined. And this is where something can tell us right away that, okay, data is still undefined in this moment in time, we are perhaps seeing an issue with how our mock providers actually specifying the mock data.
[29:57 - 30:04] And this isn't just a direct way of solving the issue, but I'm just trying to point out what I was doing to figure out what was going on. I did some googling.
[30:05 - 30:12] I did some searching. I spent like an hour or so and then I realized the request specification here expects a request that matches exactly what you're doing.
[30:13 - 30:18] The component, it's not just a request. It's literally an direct identical request to what's happening in the component .
[30:19 - 30:26] So by doing this again, and if you had those console logs, you'll see that data is then available and your test now passes. Cool.
[30:27 - 30:38] So now we only have a single test remaining and that's basically the test to specify that nothing is shown. And when you say nothing here, I guess, we're basically saying it renders or to better control to what we're saying here.
[30:39 - 30:51] It does not render the loading section or listings or the listing section when query has an error. And this is a suitable test.
[30:52 - 31:04] We want to say if the query has failed, we don't want to continue to see the loading section. And if the query has failed, we probably shouldn't be seeing anything with regards to the UI saying premium listings.
[31:05 - 31:14] So in this particular test, the last test we're going to work on right now, it 's going to be very similar to the one above. And so what does first copy everything we have here that prepares the test above?
[31:15 - 31:20] And we'll first discuss what are we trying to do with our assertion. Our assertions in this instance are going to be a little different.
[31:21 - 31:29] Our assertion is the fact that neither of those nodes we were checking for before should exist. So in other words, they should both be no.
[31:30 - 31:38] What is the change we're going to do in this test to facilitate this expectation? It's going to be how we expect the data to be returned or in other words, not to be returned, right?
[31:39 - 31:45] So instead of saying maybe results, data exists, we can perhaps say there is no result. There is no data.
[31:46 - 31:49] And instead, there is a network error. There's actually a request error that happened.
[31:50 - 32:07] And we can just specify a new error here and we can provide just some random information that says network error. If we were to save our changes and if we were to go to our terminal, we should expect that test to pass.
[32:08 - 32:11] And it passes. Amazing.
[32:12 - 32:27] So now it tells us that if loading isn't happening any longer and if data doesn 't exist or more particularly if perhaps a network error has occurred, we should not see the nodes that we expected to see above. A quick note to make here.
[32:28 - 32:32] The term error, there's many different terms to what an error could be, right? There could be a network error.
[32:33 - 32:41] There could be a GraphQL error. There could be an error in the instance of the fact that the request was successful but perhaps no data was returned.
[32:42 - 32:49] Either data was null or there was a listings object and data and that listings object was empty, right? There's many different ways to think about this.
[32:50 - 32:59] When it comes to writing robust tests, I do encourage people to think about all the different cases, right? So you want to think about the cases where your network error might occur.
[33:00 - 33:03] You want to think about cases where your GraphQL has an error. You want to think of cases where data is not populated.
[33:04 - 33:16] You want to think of cases where data might be null. So if I was to continue here, I would probably replicate this test maybe two or three more times in those different cases and try to verify neither of these sections or neither of these nodes are shown.
[33:17 - 33:32] I'm not going to do that. It's a pretty repetitive and it isn't too difficult to expand what's been done here but I do want to encourage that when it comes to writing robust tests, you do want to think about all the different cases or in more particular in this case, all the different error cases that it could ever occur.
[33:33 - 33:36] And that's it. We'll stop here.
[33:37 - 33:51] This is pretty much all the tests we wanted to write for the HOPE component. Whether there's other tests that could be written perhaps, whether there wasn't a need for all these tests perhaps, it completely comes down to the person working on these tests and what they think are suitable.
[33:52 - 33:59] In my instance, I think I wanted to test everything here that we sort of worked on. In other words, it wasn't because we use a separate library.
[34:00 - 34:06] It wasn't relevant to the fact that another thing was being implemented by a library. It was more of the custom work we set up in.
[34:07 - 34:19] Some of that custom work was how our search input works in the home page and how we handle the UI of the actual premium listings section based on the status of the query. Everything else, additionally, was pretty static, right?
[34:20 - 34:29] The fact that it says your guide for all things rental or helping you make the best decisions, this is all static information. It's independent of the fact that our query fails or is successful.
[34:30 - 34:36] As a result, I don't consider them as suitable tests. So let's quickly do a recap of the tests we've written.
[34:37 - 34:44] For the search inputs, the first specific test we looked for is we wanted to search that it was an empty search input when the component initially renders. How do we do this?
[34:45 - 35:01] We simply checked for the actual search input node and we tried to inspect the value as to be empty. Following on this, we wanted to verify that the search input works as intended and it redirects the user to the listings page when a valid search is provided.
[35:02 - 35:04] How do we do this? We filed the search input node again.
[35:05 - 35:25] We fired two events, a change event to trigger an actual typing of the input and an inter-key down event to trigger the fact that the user submitted the input. As a result, we then asserted or expected our assertion such as that the history location path name or the location the user is in is now listing slash whatever the user searched for.
[35:26 - 35:38] Similarly, we wanted to verify that if the user provided invalid inputs, just an empty string, they are not redirected to the listings page. Those were the tests that we covered for the search input.
[35:39 - 35:58] For the premium listing section, we wanted to figure out, I guess, inspect the fact for how the UI of our premium listings was to behave depending on the status of the query. When this query was loading, we expected that the node that was to say premium listings loading was to be available or to be shown as opposed to being null.
[35:59 - 36:16] We expected the other node that simply just says premium listings to be null, to not be shown. When data is available, in our assertions, we specified that the node in which premium listings are shown is to not be null and the node that just simply says loading as well is to be null.
[36:17 - 36:35] In other words, we wanted to see the section that shows listings and not the section that shows loading or the skeleton UI. Last but not least, when an error has occurred or when there's no data and the query is not loading any longer, we expected that neither of those nodes that we looked at before is to be there.
[36:36 - 37:08] In other words, they're both to be null. One of the things we've done in almost all of our tests, we took advantage of the Marked Provider component and this allowed us to specify a Mark GraphQL requests from our components and even for the tests that were independent of the fact of the query, we still needed to do so because our components will still be unaware of what use query was or what any of these functionality was since our component's whole is part of the Apollo provider setup that we've done before.
[37:09 - 37:31] Since our home component also uses a link component and route components, it needs to be within the context of React Router and to do so, we also wrapped each of our home components with a router components and just specified a history object that initially is just standard and up to a while allows us to inspect if there's any changes to path name in this history. That's pretty much it.
[37:32 - 37:43] This is pretty much everything we wanted to do here and we've tested the home component pretty thorough. We did another small thing here just to remove that console warning around scrolling since it's not relevant to what we're trying to do for our tests.
[37:44 - 37:48] You can see here that this looks pretty polluted now or not polluted, I guess, polluted is the wrong word. It looks pretty populated.
[37:49 - 38:00] There's even though there isn't a lot of tests, maybe let's say 4, 5, 6, we see that there's a lot of blocks for each test. I mentioned this in the beginning of either this lesson or the lesson before.
[38:01 - 38:18] There's different ways people like to do their tests. One of the approaches people tend to do is they like to group a lot of functionality into some utility function that can be used or there's another thing that can have to be used that I didn't talk about is a function that just provides called before each.
[38:19 - 38:36] In this particular function, just allows us to essentially specify what will happen before every single block below it is to run. Maybe we can do this in the very first describe block or similarly, you can also have it before each before every unit test within a particular block.
[38:37 - 38:48] Oftentimes, I've seen people sometimes, this is where they'll group some of their functionality. For example, this is where they'll maybe create a certain query here that can be used in the test that follow.
[38:49 - 38:58] However, whenever you use before each function, it's important to keep in mind that you should try to clean up what you do before you do the other test. You don't want a certain test to affect another.
[38:59 - 39:03] There's a few things. I don't want to go down that rabbit hole, but that's one functionality that can be done.
[39:04 - 39:27] Similarly, I've seen people also use functions and just utility functions to be like this is going to be a function that allows you to specify the home components in test. This is a bad name, but the idea is instead of replicating this over and over and over, you can just simply just copy this right here and put it in a function of sorts , have it returned the entire setup that we're looking for, take an argument.
[39:28 - 39:38] The arguments could be the mock graph code request, feed it in here, and we can just use that instead of just creating this small set of nested components. There's no right or wrong.
[39:39 - 39:46] I mean, it's completely dependent on how you do it. The pattern that I've been following lately is being more verbose in my tests as opposed to calling utility functions.
[39:47 - 39:54] It's helped me avoid leakage of information between tests. It's a little bit more populated, but it's easier to see.
[39:55 - 40:12] In my opinion, this is just a personal opinion, if the test like this isn't so difficult to see, it's pretty apparent what's happening even though it looks like there's a decent number of lines, as long as that's the case as totally fine, and I tend to continue and I try to replicate this information as much as I want. This is a bit of a rant.
[40:13 - 40:27] I just wanted to mention that there's many ways of setting up your test structure, and there's no right or wrong ways. This is just what I followed, but you're more than welcome to set it up the way you please for you to make it more utility focused, make it more, I guess, broken down.
[40:28 - 40:29] It's completely up to you. Awesome.
[40:30 - 40:31] Great work so far. I'll see you in the next lesson.