The Importance Of Testing And How To Write Tests With React-Testing-Library

nrymarz

Nathan Rymarz

Posted on June 15, 2021

The Importance Of Testing And How To Write Tests With React-Testing-Library

In my time at Flatiron, we used tests as guidelines for apps that we were building but we never learned to write our own. However, as part of a coding exercise for a company I had applied to, I was required to build a web application which included unit testing. This led to me learning about the react testing library, mocking functions like fetch and the overall importance of testing.

In larger applications, writing tests is vital to save time because manually testing every function of an app to ensure nothing is broken could take extremely long and is prone to human error. And more than that, tests also act as documentation. For someone who is new to a codebase, reading the tests can help them get caught up with which parts of a application are responsible for which features and how they are expected to work.

To start writing tests in a React app, we create a file with the .test.js extension. Inside our test file we need to import a couple things.

import { render, fireEvent } from '@testing-library/react'
import "@testing-library/jest-dom/extend-expect"
Enter fullscreen mode Exit fullscreen mode

The render method allows us to test a certain component by rendering it in a virtual DOM. After rendering it we can write an expect statement. For instance...

const app = render(<App/>)
expect(app).not.toThrowError
Enter fullscreen mode Exit fullscreen mode

The above 2 lines create a DOM with the component App rendered inside it and expects that the app should throw no errors. However, to be able to use these lines to really test your app, we need to wrap them inside a test method.

test("App renders without errors", () =>{
    const app = render(<App/>)
    expect(app).not.toThrowError
})
Enter fullscreen mode Exit fullscreen mode

The test function takes 2 parameters. The First one being a string that describes the test, and the second being a callback function that runs the test.

Now, if you used create-react-app to build your React application, you will be able to run this test by typing 'npm run test' in the console.

We can also write tests that simulate user actions with the fireEvent method that we imported earlier. FireEvent is an object of functions. So to make use of fireEvent we use dot notation to call the event that we want to simulate and pass the element we want to act on as an argument. For example...

const app = render(<App/>)
const btn = app.getByTestId("btn")
fireEvent.click(btn)
Enter fullscreen mode Exit fullscreen mode

In this case, we use fireEvent.click to simulate a user clicking on a button.

Lastly, a common scenario you might face is writing tests for a component which fetches external data. To do this we first need to do a few things.

We need to to write our own version of the fetch method. This is commonly called creating a "mock." Here is the code I wrote to mock fetch.

global.fetch = () =>{
    return Promise.resolve({
        json: () => Promise.resolve([
            {example:"data"},
            {example:"more data"}
        ])
    })
}
Enter fullscreen mode Exit fullscreen mode

Our mock fetch will return some JSON that you can specify. The nice thing here is that we aren't actually making any http requests so we always know how our fetch will respond.

Furthermore, in addition to importing fireEvent and render, we also need to import 'act' and 'screen.'

import { render, fireEvent screen, act} from '@testing-library/react'
Enter fullscreen mode Exit fullscreen mode

'act' will help us with writing async functions to use our mock fetch with and 'screen' will be what we use to access our virtual DOM.

Here is an example of a test that uses all of these and expects our JSON data to be rendered on our page and be defined.

test("App fetches some JSON", async () =>{
    await act( async () => render(<App/>))
    const renderedData = screen.getByTestId("rendered-data")
    expect(renderedData).toBeDefined()
})
Enter fullscreen mode Exit fullscreen mode

For this test to work our App component needs to be rendering the JSON data and also give that rendered data a test-id of 'rendered-data.' Also notice that we use the async and await keywords because our function has to be asynchronous since we have to wait for our fetch to return our data before we assign renderedData. That is also why we have to wrap our call to render App inside the 'act' function.

In conclusion, there are many more things to know about writing tests but I hope that reading this gives you a headstart into writing your own tests for your personal projects. I believe having experience writing tests may help you stand out to an employer and get that foot in the door as a new professional programmer.

💖 💪 🙅 🚩
nrymarz
Nathan Rymarz

Posted on June 15, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related