Setting up tests with mocks and more mocking examples (Jest mocking + React part 4)
Tags: jest, mocking, react
Published: 2022-09-08 14:59:56

In this last part of the series I want to tie off some loose ends. Things I wanted to talk about earlier but didn't really fit into the structure of this series.

We will start by looking at some caveats when running tests and then look at one more example of using mocks when testing `React` components.

1. Setting up tests with mocks
2. Mocking render props

*The examples I use in this article are available on [github](https://github.com/peterlidee/mocking-app) ([src/part4](https://github.com/peterlidee/mocking-app/tree/main/src/part4)). These files a build upon create-react-app so you can run them using `npm run start` or run the tests using `npm run test`.*

## 1. Setting up tests with mocks

Using mocks in tests can lead to problems. When running a test on a function or component you will often find yourself running multiple test cases. But this will influence your mocks. Remember that mocks record their own behaviour. So, when a mock got called multiple times in different tests their logs will include previous calls and this is undesired. An example:

```jsx
// part4/example1/ChildComponent.js
function ChildComponent(props){
  return(
    <div className="ChildComponent">
      Child component says {props.message}
    </div>
  )
}
export default ChildComponent
```

```jsx
// part4/example1/ParentComponent.js
import ChildComponent from "./ChildComponent"

function ParentComponent(props){
  return(
    <div className="ParentComponent">
      <div>Parent Component</div>
      <ChildComponent message={props.message} />
    </div>
  )
}
export default ParentComponent
```

The parent takes a message prop and then the child prints the message. When testing the parent, we mock the child and we run multiple tests:

```javascript
// part4/example1/__tests__/test1.js
import { screen, render } from '@testing-library/react'
import ParentComponent from '../ParentComponent'
import ChildComponent from '../ChildComponent'

jest.mock('../ChildComponent')

test('Parent renders correctly', () => {
  render(<ParentComponent />)
  expect(screen.getByText(/Parent component/i)).toBeInTheDocument()
})

test('ChildComponent mocks gets called correctly', () => {
  render(<ParentComponent />)
  expect(ChildComponent).toHaveBeenCalled()
  // fails
  expect(ChildComponent).toHaveBeenCalledTimes(1)
})
```
The second test fails:

```
● ChildComponent mocks gets called correctly

    expect(jest.fn()).toHaveBeenCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 2
```
Because the mock of the child was actually called 2 times. We rendered once in the first test and again in the second. Hence, 2. But, as said, this is undesirable. We don't want to let results from previous tests pollute other tests. So, how do we solve this?

An obvious way could be to call `jest.mock` inside our test. But this is not allowed. `Jest` mocks modules by hoisting the mock above the import (I don't fully understand this myself). But, `Jest` can't hoist when the `jest.mock` statement is inside the test.

That is why `Jest` provided [some more helpers](https://jestjs.io/docs/jest-object): the most important are `.clearAllMocks()` and `.resetAllMocks()`.

### 1.1 `.clearAllMocks()`

When calling `jest.clearAllMocks()` the 'logs' of all the mocks are cleared of the data the mocks got called with. This means that `ChildComponent.mock.calls` got reset to `[]`. `Jest` helper functions like `.toHaveBeenCalledTimes()` also got reset. A new test to demonstrate this:

```javascript
// part4/example1/__tests__/test2.js
import { screen, render } from '@testing-library/react'
import ParentComponent from '../ParentComponent'
import ChildComponent from '../ChildComponent'

jest.mock('../ChildComponent')

test('Parent renders correctly', () => {
  render(<ParentComponent message="Hello" />)
  expect(screen.getByText(/Parent component/i)).toBeInTheDocument()
})

test('ChildComponent mocks gets called correctly', () => {
  // clear
  jest.clearAllMocks()
  render(<ParentComponent message="Hello" />)
  expect(ChildComponent).toHaveBeenCalledTimes(1)
  expect(ChildComponent).toHaveBeenCalledWith(
    expect.objectContaining({
      message: 'Hello'
    }),
    expect.anything()
  )
})

test('ChildComponent mock got reset', () => {
  // clear
  jest.clearAllMocks()
  expect(ChildComponent).toHaveBeenCalledTimes(0)
  expect(ChildComponent.mock.results).toHaveLength(0)
})
```

Besides the `.clearAllMocks()` method there is also a `mockClear` method that can be called on an individual mock.

### 1.2 `.resetAllMocks()`

This method does the same as the clear method: it resets the calls (and the instances). But on top of that it also resets the results or the return value of the mock.

```javascript
// part4/example1/__tests__/test3.js
import { render } from '@testing-library/react'
import ParentComponent from '../ParentComponent'
import ChildComponent from '../ChildComponent'

jest.mock('../ChildComponent')

test('Child returns foo', () => {
  ChildComponent.mockReturnValue('Foo')
  render(<ParentComponent message="Hello" />)
  expect(ChildComponent).toHaveReturnedWith('Foo')
})

test('Child returns nothing', () => {
  // reset
  jest.resetAllMocks()
  expect(ChildComponent.mock.results).toHaveLength(0)
})
```

Besides the `.resetAllMocks()` method there is also a `mockReset` method that can be called on an individual mock.

### 1.3 beforeEach()

As you probably already guessed, the `beforeEach()` global jest method is ideal for running clearing or resetting mocks.

### 1.4 tool presets

Because clearing and resetting mocks using `.beforeEach()` or `.afterEach()` is such a common pratice, a lot of tools like `create-react-app` have a build-in settings for this. `create-react-app` has a default setting of resetting beforeEach. This caused me a lot of confusion while writing this article because I'm used to `create-next-app` that has a different default setting.

I ended up changing the setup of the example files of this series (build with `create-react-app`) by adding this rule `"jest": { "resetMocks": false }` to the [package.json](https://github.com/peterlidee/mocking-app/blob/main/package.json) file.

___

## 2. mocking render props

As a the final section in this series, we will go over the render props patterns and how to test it with mocks.

### 2.1 About render props

Let's take this example:

```jsx
// part4/example2/FetchComponent.js
import useFetch from 'react-fetch-hook'

function FetchComponent(props){
  const { isLoading, data, error } = useFetch(props.url)
  return props.children({ isLoading, error, data })
}
export default FetchComponent
```

We have a `FetchComponent` that takes a url and a child as props. It fetches the url and returns the results to a child component by calling props.children as a function, passing in the results of this fetch: `props.children({ isLoading, error, data })`. 

`FetchComponent` takes children as a prop and then returns children with some extra data.

```javascript
// component receives
prop.children
// component returns
props.children(data)
```

This is called render props pattern and it is used to pass data to any child. The catch when using this technique is that the child of this `FetchComponent` has to be a function, not a component. Here is an example of that:

```jsx
// part4/example2/UsersComponent.js
import FetchComponent from "./FetchComponent";

function UsersComponent(){
  const url = `https://jsonplaceholder.typicode.com/users/`
  return(
    <FetchComponent url={url}>
      {({ isLoading, error, data }) => {
        if( isLoading ) return '...loading'
        if( error ) return 'Error'
        return data.map(user => <div key={user.id}>{user.name}</div>)
      }}
    </FetchComponent>
  )
}
export default UsersComponent
```

So here is our child: `UsersComponent`. It makes a call to jsonplaceholder/**users**, hence **`Users`**`Component`. Our `UsersComponent` wraps it's content inside `FetchComponent` that, as we know returns a function. In our users component you can see that we provided a function as child: 

```jsx
({ isLoading, error, data }) => {
  // function body
}
```

Inside the function body, `UsersComponent` now has access to the data from the fetch component.

### 2.2 Testing UsersComponent

We start with testing the `UsersComponent`. What do we need to do? Mock the `FetchComponent` because we want to test `UsersComponent` in isolation. But, our `UsersComponent` relies on the return value of the `FetchComponent`. So, we have to add a return value on the mock. What does the fetch component return? A function.That's easy:

```javascript
// mock the component
jest.mock('../FetchComponent')
// return a function
FetchComponent.mockImplementation(() => {})
```

Now, let's add some values to this function. `FetchComponent` returns `props.children(somedata)`.

```javascript
FetchComponent.mockImplementation(
  (props) => props.children()
)
```
And then as a final step, we add some data that we just manually write. What data? The results from the fetch hook:

```javascript
FetchComponent.mockImplementation(
  (props) => props.children({
    isLoading: true,
    error: false,
    data: undefined,
  })
)
```
And that's all. Let's go over this again. We are mocking the return value from `FetchComponent`. We know that `FetchComponent` returns `props.children(data)` so we returned that from our mock using some made up data. The mock of `FetchComponent` now returns what is required and we can run the test on our `UsersComponent`.

Below is the full test file. We simulate different scenarios: a loading state, an error state, ... and run tests on each scenario.

```javascript
// part4/example2/__tests__/UsersComponent.test.js
import { render, screen } from '@testing-library/react'
import UsersComponent from '../UsersComponent'
import FetchComponent from '../FetchComponent'

jest.mock('../FetchComponent')

beforeEach(() => {
  jest.resetAllMocks()
})

test('UsersComponent renders correctly with loading state', () => {
  FetchComponent.mockImplementation(
    // eslint-disable-next-line testing-library/no-node-access
    (props) => props.children({
      isLoading: true,
      error: false,
      data: undefined,
    })
  )
  render(<UsersComponent />)
  expect(FetchComponent).toHaveBeenCalled()
  expect(screen.getByText(/...loading/)).toBeInTheDocument()
})

test('UsersComponent renders correctly with error state', () => {
  FetchComponent.mockImplementation(
    // eslint-disable-next-line testing-library/no-node-access
    (props) => props.children({
    isLoading: false,
    error: true,
    data: undefined,
  }))
  render(<UsersComponent />)
  expect(FetchComponent).toHaveBeenCalled()
  expect(screen.getByText(/Error/)).toBeInTheDocument()
})

test('UsersComponent renders correctly with no isLoading, no error and data', () => {
  FetchComponent.mockImplementation(
    // eslint-disable-next-line testing-library/no-node-access
    (props) => props.children({
    isLoading: false,
    error: undefined,
    data: [
      { name: 'Foo', id: '1' },
      { name: 'Bar', id: '2' },
    ],
  }))
  render(<UsersComponent />)
  expect(FetchComponent).toHaveBeenCalled()
  expect(screen.getByText(/Foo/)).toBeInTheDocument()
  expect(screen.getByText(/Bar/)).toBeInTheDocument()
})
```

The essential take away of this test if that we return a function from our mock because the component also returns a function.

### 2.3 Testing FetchComponent

Let's now test the `FetchComponent`. Here is that component again:

```jsx
// part4/example2/FetchComponent.js
import useFetch from 'react-fetch-hook'

function FetchComponent(props){
  const {isLoading, data, error} = useFetch(props.url)
  return props.children({ isLoading, error, data })
}
export default FetchComponent
```

Obviously, we want to mock `useFetch`. We also want to return results from this mock: isLoading, error and data. Let's do that:

```javascript
// mock
jest.mock('react-fetch-hook')
// return results
useFetch.mockReturnValue({
  isLoading: true,
  error: undefined,
  data: undefined,
})
```
Notice how we are simply returning an object with 3 properties using `.mockReturnValue()`. We are not returning a function because that is not what `useFetch()` does.

What else do we need? `FetchComponent` returns something. So, when we test `FetchComponent`, we want to test if it actually returns something. 

Remember that `FetchComponent` receives a child then returns that child with some extra data:

```javascript
// component receives
prop.children
// component returns
props.children(data)
```

But, when we test `FetchComponent` in isolation, there is no child. So, we will mock a child. What does this child look like?

```javascript
<FetchComponent url={url}>
  {({ isLoading, error, data }) => {
    // function body
  }}
</FetchComponent>
```

It is a function, that takes the data and then returns something. Does the child mock need a return value? No. We are interested in what the child gets called with because that is part of the functionality of `FetchComponent`. The return value of child is of no use. Let's now mock the child using a mock implementation:

```javascript
// the mock
const ChildMock = jest.fn((props) => null)

// the render
render(
  <FetchComponent url="dummy">
    { ChildMock }
  </FetchComponent>
)
```
And that is all we need. `ChildMock` takes the data passed by fetch and simply returns nothing. Here is the full test:

```javascript
// part4/example2/__tests__/FetchComponent.test.js
import { render } from '@testing-library/react'
import FetchComponent from '../FetchComponent'
import useFetch from 'react-fetch-hook'

jest.mock('react-fetch-hook')
const ChildMock = jest.fn(props => null)

test('FetchComponent mocks useFetch correctly', () => {
  useFetch.mockReturnValue({
    isLoading: true,
    error: undefined,
    data: undefined,
  })
  render(
    <FetchComponent url="dummy">
      {ChildMock}
    </FetchComponent>
  )
  // check the useFetch mock
  expect(useFetch).toHaveBeenCalledWith('dummy')
  expect(useFetch).toHaveReturnedWith(
    expect.objectContaining({
      isLoading: true,
      error: undefined,
      data: undefined,
    })
  )
})

test('FetchComponent ChildMock works correctly', () => {
  expect(ChildMock).toHaveBeenCalledWith(
    expect.objectContaining({
      isLoading: true,
      error: undefined,
      data: undefined,
    })
  )
})
```
I hope these tests make sense. Explaining how to test render props is quite difficult. If you couldn't quite follow I recommend you reread previous section or try writing a test for these components yourself.

## Conclusion

We talked about a lot in this series. We started with a general introduction of `Jest` mocks: why and how to use mocks. We then moved on to using mocks to test `React` components. We saw how to mock a module and different ways to run tests on mocks. We also saw how to setup mocks so they return values and why you need to do this. We used different `React` patterns to demonstrate all of this.

All of this has given you a good understanding of using `Jest` mocks to test `React` components. Although there is much more to learn you will now be able to integrate this into what you learned here.

I hope you enjoyed this series and happy testing!