Our unit tests should never rely on code that is outside our control and they certainly shouldn't be calling across the network to a server. A good test needs to be repeatable and fast and relying on outside systems and/or data doesn't help us meet those goals. In this lesson, we'll use mocks to create an environment where we control the response for service calls. This will allow us to be confident in our assertions and ensure our tests aren't flaky.
Instructor: [0:00] Let's start by adding a new file in source called app.spec.js. This is where we're going to write our unit tests for our app component. I'll start by importing React from React. I'm also going to import render and the act utility from testing library. I'm going to import my app component because that's what I want to test.
[0:32] Then I'm going to drop down and let's create our test. This is going to be it renders cards from the API. I'll define my callback function here. Let's start by rendering the app. Then I'm going to destructure the results of this. The first thing I'm going to get is a debug function. I'm just going to call debug right here. I'll save this and then in the terminal I'm going to stop the app.
[1:09] Let's start the test with yarn test. Our test is going to pass because we don't have any assertions, but if we look at the output from debug we'll see that we don't have any of our items. That's because this test is running before that async call can return.
[1:27] The other problem with this test is that it's going to try to make the async call and we don't want to do that. We don't want to rely on an external server. Let's see if we can clear all this up by mocking out our service module and providing a known result to our component when it makes that fetch call.
[1:45] I'll get the terminal out of the way. I'm going to come up to the top of my test and I'm going to start by defining some mock data. I'm just going to do a const mock parts. This is going to be an array of fake card data. I'm just going to paste that in. We just have three objects, each with an ID, a term, and a definition that matches the shape of our cards.
[2:08] Now I'm going to come outside of my test and I'm going to call test dot mock. My mock is going to point to the surfaces slash card service module. I'm also going to import. I'm going to import get cards from service slash card service.
[2:34] When this runs Jest is going to hoist this mock call up to the top of our file. You can imagine if we wrote it up here, this is going to hook up into the module cache. When this import runs, get cards is going to come back as a Jest mock function. This will happen at run time, so we can move this back down here and leave our imports at the top. That's how this is going to work.
[2:58] By importing this, we get a reference to that mock function that we can use down in our test. I'll come down here and get cards is now going to have this mock resolved value method on it. I'm going to pass it the mock cards array. Now when app renders and the use effect fires the call to get cards, it will resolve this mock cards array.
[3:23] Our mocking is in place, but we're still not handling the async nature of this component making this API call and then re-rendering. We're going to start at the top by making this an async function. Then I'm going to come down here and I'm going to move the render into a call to act. Act can be async.
[3:45] We'll move this up and we'll make this an async function and we'll add an await before the call to act. In order to get a reference to this debug function or any of the other queries that are returned from render, we need to define them outside of this async function and then assign them in here.
[4:03] I'm going to come up here and I'm going to do a let debug. Then down here I'm going to remove this const and in order to destructure into an existing identifier we just need to surround this entire call in parentheses.
[4:19] I'm going to save this. Let's open up the terminal and let's look at our test run. We'll see that it passes again. We still have no assertions, but as we can see here we've now rendered out our mocked data with questions one, two, and three.
[4:36] Our debug is showing us that the newly rendered output is giving us exactly what we expect using our mock data for get cards. Now that we know this is working, let's make some updates.
[4:49] I don't really need this debug call, so I'm going to replace this with get by text. Then I'm going to assign get by text here. Then after my render call that's inside the act, we can make some assertions.
[5:04] I'm going to actually just grab mock cards which is our array that's defined for our mock data. I'm going to for each over them. For each one of these cards I'm going to make an assertion. I'm going to expect that get by text card dot term is in the document.
[5:31] I'll save this and we'll look at the test again. Now the test passes and it's passing with assertions in it, so we know everything is good.