Testing Frontend Applications: User Story 1 - Testing app initial load (Part 3B)

Tests to carry out the first time user loads our application

ยท

7 min read

Testing Frontend Applications:  User Story 1 - Testing app initial load (Part 3B)
OTHER ARTICLES IN SERIES


Application we are testing: Post Feed Application

GitHub repository: Post Feed Application Code

Without wasting much time let's get right to it. To easily point out the line in the code I am explaining, I would be making use of a tool called Carbon.

We have outlined what we want to test, and why we want to test it in our previous article so If you haven't checked that out, I suggest you do so, as it would definitely give you more insights as to why certain decisions are going to be taken in this article. What should I test? Part A

test_1.png

Structure before network request resolves

Line 1 : We import the component we like to test

Line 2 : We import the react testing library render method; it is what we would use to render our component on the jest dom. I will explain where we are importing it from (i.e ../utils/test) shortly. However, you can see the utils/test.tsx file here

Line 5 : We create a block to group our tests using describe, thereafter we give it a short description "User"

Line 6 : We create another block which would contain an individual test using it keyword and also give it a short description "can see posts when they open the application".

When our tests run, our console would append the description in the describe block to that in the it block, which would then output a full description that reads User can see posts when they open the application. This makes it very easy for us to identify which tests fail or pass from simply looking at the description in our console.

You would notice that the second parameter of the it block which is a function, has the async keyword. I would explain why shortly.

Line 7 : We render our component

Line 9 : We check to make sure that the text "Post Feed" is in the document using the screen.getByText query

Line 11 : We check that an input with the name "post" is also in the document. Here we use the screen.getByRole query.

When it comes to which query (i.e what we use to select our elements) to use to write tests , always try as much as possible to go for the accessible queries. You can see the priority of which query to use here

Using getByRole ensures that we are testing our app in an accessible way, which in turn, means that screen readers would also be able to detect what we are testing. By default, any html element that allows input (e.g input, textbox) is given a default role of textbox by the browser. see here.

Line 13 : We check that a text "Loading..." is in the document

These are the initial checks we do to ensure that the default display of our application is as we expect.

Structure during and after network request resolves

Next, we need to check that the loading text disappears and we can now see a list of posts. We know that when we see the loading text in our application, a network request is being made. However when writing tests, we usually do not want to make an actual network request but instead mock the network requests. This gives rise to two important questions:

What does it mean to mock a network request?

In English, to mock something simply means to make a copy of it that is not actually real. It is usually used to simulate the real experience, e.g a mock examination. Therefore, mocking network requests simply means making a network request that is not actually real but simulates the real experience.

Why do we mock network requests?

  • Reduce the time taken for our tests to run.

When we run our tests without mocking network requests, an actual API call is made to our backend server and this could take time - depending on when the response comes from our server. We do not need to waste this time since we already know the structure of data our backend ought to return.

  • Testing in a predictable environment makes us write better tests

To the best of our ability, we always want to test in a predictable environment. We know that an API call to the backend could either resolve successfully or fail. Knowing these two states, we can simply write tests to suite their response.

We would be using axios for our network requests, so let's create a simple axios mock. These days, it's more adviceable to just simply use Mock Service Worker rather than create your own mocks. However at this point in our application we will just create our own mock since it is very simple.

Open the command line and run this command to create a mocks folder in the root of your app.

mkdir __mocks__

By default, jest goes through our app and automatically detects any file inside the __mocks__ folder as a mock. See resource

Next, we create an axios mock file inside the mocks folder we just created. Go to the root folder of your application and run

touch __mocks__/axios.js

Now all we need to do is fill up our axios mock file with the following:

const axios = {
    get: jest.fn().mockResolvedValue({ data: [] }),
    post: jest.fn().mockResolvedValue({ data: [] }),
    delete: jest.fn().mockResolvedValue({ data: [] }),
    put: jest.fn().mockResolvedValue({ data: [] }),
};

export default axios;

What are we doing in this file?

We create an object containing the four axios methods we will be using in our application. In each of them, we map their keys to a jest function(i.e jest.fn()), then we create the resolved value we want for a request that matches any of our keys using mockResolvedValue. For no particular reason, we just put in an object with a data field. Place whatever you want the result of your axios call to be as an argument to mockResolvedValue. Whenever axios detects a call with that particular method (i.e get/post/delete/put) it would return whatever argument was passed into mockResolvedValue.

Now let's modify our initial test to accommodate the network request that occurs when the loading text is on the user's screen.

test_3.png

We have modified our test by adding some new lines so let me explain what is going on.

Line 3 : We import axios which we would be using for our network requests.

Line 4 : We import some data. This data would be the value we "assume" our backend would return if an API call is successful. Only the "structure" of the data has to be the same, the values can be anything.

On the initial start of our application an axios get call is made. Jest intelligently goes through the __mocks__ folder to see if there is a mock for our axios call. Since one exists, jest intercepts the real axios call with our mock axios call and immediately returns what we passed into our mockResolvedValue in our mock file. In our axios mock file, we returned a resolved value of an object with a data key being mapped to an empty array, but since we don't want an empty array returned in our actual test, we need to override the default axios.get.mockResolvedValue in our mock file. That is exactly what we are doing in Line 8. Instead of returning an empty array we are returning a list of data we call postData. You can see our postData here

Line 8 : We make sure that the response of our get request is our post data

Line 18 : We wait for the element with the text "Loading..." to be removed from the document by using the waitForElementToBeRemoved helper function, which is asynchronous (hence the need for the await keyword).

Line 20 : We check that the axios.get function has been called

Line 23 : We then assert that we can now see 10 posts on the document. We retrieve all elements that have the article role (recall that we are using accessible queries)

๐ŸŽ‰ We have successfully tested the initial loading of our application

Let's move on to testing post creation

Next => #3C: User can create a post
ย