The Importance Of Testing And How To Write Tests With React-Testing-Library
Tags: react, testing, beginners, javascript
Published: 2021-06-15 02:16:51

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.
```javascript
import { render, fireEvent } from '@testing-library/react'
import "@testing-library/jest-dom/extend-expect"
```
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...
```js
const app = render(<App/>)
expect(app).not.toThrowError
```
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.
```js
test("App renders without errors", () =>{
    const app = render(<App/>)
    expect(app).not.toThrowError
})
```
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...
```js
const app = render(<App/>)
const btn = app.getByTestId("btn")
fireEvent.click(btn)
```
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.
```js
global.fetch = () =>{
    return Promise.resolve({
        json: () => Promise.resolve([
            {example:"data"},
            {example:"more data"}
        ])
    })
}
```
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.'
```js
import { render, fireEvent screen, act} from '@testing-library/react'
```
'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.
```js
test("App fetches some JSON", async () =>{
    await act( async () => render(<App/>))
    const renderedData = screen.getByTestId("rendered-data")
    expect(renderedData).toBeDefined()
})
```
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.