Mocking React components (Jest mocking + React part 2)
Tags: jest, mocking, react
Published: 2022-09-08 14:52:49

In the [first part](https://dev.to/peterlidee/introduction-to-jest-mocking-jest-mocking-react-part-1-29e) of this series we talked about what a mock is, why you need mocking and how to set up mocks.

In this part, we will be applying the things we learned in part 1 to testing `React` components. We start of by illustrating why you need mocking and continue with actually mocking some components.

1. Why you need mocking to test React
2. How to mock React components
3. Mocking components that have props

*The examples I use in this article are available on [github](https://github.com/peterlidee/mocking-app) ([src/part2](https://github.com/peterlidee/mocking-app/tree/main/src/part2)). 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. Why you need mocking to test React

`React` components are always linked/nested to other components or packages. But, when testing a component, you want to test that component only. Therefore, you need to somehow isolate it from the rest. This is where Jest mocking comes into play. Here is an example:

```jsx
// part2/example1/ChildComponent.js
function ChildComponent(){
  return(
    <div className="ChildComponent">
      Child component
    </div>
  )
}
```

```jsx
// part2/example1/ParentComponent.js
function ParentComponent(){
  return(
    <div className="ParentComponent">
      <div>Parent Component</div>
      <ChildComponent />
    </div>
  )
}
```
Our goal is to test `ParentComponent`. With no mocking it would look like this:

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

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

As expected, this test renders both the parent and the child components. We can test this by adding `screen.debug()` (prints output in console) or by adding another expect statement.

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

test('ParentComponent and ChildComponent render', () => {
  render(<ParentComponent />)
  screen.debug()
  expect(screen.getByText(/Parent Component/i)).toBeInTheDocument()
  expect(screen.getByText(/Child Component/i)).toBeInTheDocument()
})
```

This is normal react behaviour but **not what we want when testing**! Let's say somebody else is yet to write that `ChildComponent` and you don't know anything about it. Maybe the child also contains the text "Parent Component"? This would make your test fail. (`getByText` [returns error when there is more then 1 match](https://testing-library.com/docs/queries/about/#types-of-queries))

This was a simplistic example but it makes the problem clear. When testing a component, we don't want interference from other components or modules. Yet, `React` components are **always** linked to each other. So how do we isolate a component? With a `Jest` mock.

___

## 2. How to mock React components

Let's rewrite the test. We add one rule `jest.mock('../ChildComponent')` and we update the expect statement for the child component.

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

jest.mock('../ChildComponent')

test('ParentComponent renders and ChildComponent does not', () => {
  render(<ParentComponent />)
  expect(screen.getByText(/Parent Component/i)).toBeInTheDocument()
  // notice the .not
  expect(screen.queryByText(/Child Component/i)).not.toBeInTheDocument()
})
```

### 2.1 Automatic mocks with jest.mock()

Let us now break down this new test. `jest.mock()` takes as it's first parameter the path to the file the module is in. When there is no second parameter, `Jest` performs an automatic mock. This means that it sets up a mock of the module(s) you're importing.

For this line: `jest.mock('../ChildComponent')`, `Jest` will look at any imports we make from this file, and replace these imports with `jest.fn()`.

The child was mocked. This means that the actual component was replaced by an empty mock. As we saw in the [first part](https://dev.to/peterlidee/introduction-to-jest-mocking-jest-mocking-react-part-1-29e) of this series, mock function don't return anything (unless we tell them to). This means that the content of the child should no longer be in the document and this is what we tested:

```javascript
// previous test
expect(screen.getByText(/Child Component/i)).toBeInTheDocument()
// last test: 
// notice the queryByText and the .not method
expect(screen.queryByText(/Child Component/i)).not.toBeInTheDocument()
```

We succesfully mocked `ChildComponent`.

**On a sidenote:** *we called `jest.mock()`. `Jest` mocking is confusing because it uses the terms mock and mocking to refer to different things. `jest.fn()` creates a mocking function. Then there is the `.mock` property on a mocking function that gives you access to the 'logs'. (f.e. `mockedFunction.mock.calls[0][0]`). And just now we used the `.mock` method on the `jest` global object. Sorry about this confusion but that is just how `Jest` does it.*

### 2.2 Testing the mock

But, how do we access the mock? A mocking function - `jest.fn()` - is like a 'log'. But how do we access this 'log'? 

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

jest.mock('../ChildComponent')

test('ChildComponent mock can be tested', () => {
  render(<ParentComponent />)
  // using .mock property
  expect(ChildComponent.mock.calls).toHaveLength(1)
  // using jest helpers
  expect(ChildComponent).toHaveBeenCalled()
})
```
Notice that we now imported `ChildComponent` into our test. When we run the test, the `jest.mock` does it's automocking thing and we can then access the mocking function (the `jest.fn()`) by simply calling `ChildComponent`  as we did on the last 2 expect statements: 

```javascript
// using .mock property
expect(ChildComponent.mock.calls).toHaveLength(1)
// using  jest helpers
expect(ChildComponent).toHaveBeenCalled()
```

`ChildComponent` now behaves exactly the way we expect a `jest.fn()` to behave. We can inspect it's `.mock` property (the 'logs') or we can call the `Jest` helper methods on it like `.toHaveBeenCalled()`. 

### 2.3 expect(element) vs expect(function)

Be carefull what matcher functions you use when mocking. The matcher `.toBeInTheDocument()` for example only works on dom elements. A mock is a function. You can only call matchers that expect a function like `.toHaveBeenCalled()` on a mocking function.

### 2.4 The final test

A quick recap. To mock a `React` module we use `jest.mock(path)`. This is called an automatic mock. It automatically mocks the component. You can access this mock by simply calling it's name after you imported it.

This is the definitive test for our `ParentComponent`:

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

jest.mock('../ChildComponent')

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

test('ChildComponent mock was called', () => {
  render(<ParentComponent />)
  expect(ChildComponent).toHaveBeenCalled()
})
```

In case you are wondering why we test the mock: the parent component renders this child. Eventhough we don't want to render the child because we want to test the parent in isolation, we still expect the child to be there. That's why we simulate or mock the child.

___

## 3. Mocking components that have props

Until now, we have been working with simplistic example components. What happens when we add a prop to our child?

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

```jsx
// part2/example2/ParentComponent.js
import ChildComponent from "./ChildComponent"
function ParentComponent(){
  return(
    <div className="ParentComponent">
      <div>Parent Component</div>
      <ChildComponent message="Hello" />
    </div>
  )
}
export default ParentComponent
```

In the parent we call `ChildComponent` with a prop message and then the child returns: "Child component says hello". 

Again, we want to test the parent while mocking the child. This time we want to test if the mocked child gets called with the correct props. But what does the child get called with? In `React`, you can call components as functions. These two have the exact same result:

```jsx
<MyComponent prop1="foo" prop2="bar" />
// equals
{MyComponent({ prop1: "foo", prop2: "bar" })}
```

From this, it also becomes clear what a component gets called with: an object with all the props. So, we expect the `ChildComponent` mock to get called with `{ message: 'hello' }`.

### 3.1 .toHaveBeenCalledWith() on js function mocks

We saw how `.toHaveBeenCalledWith()` works on an javascript function in [part1](https://dev.to/peterlidee/introduction-to-jest-mocking-jest-mocking-react-part-1-29e) of this series but I will quickly refresh this:

```javascript
function doAThing(callback){
  callback('foo')
}
```

We have a simple function. It takes a callback function as argument and calls this callback with the argument 'foo'. In a test, we would mock callback and then test if this mock gets called with the correct argument.

```javascript
test('MockCallBack gets called with the correct argument', () => {
  const mockCallback = jest.fn()
  doSomething(mockCallback)
  expect(mockCallback).toHaveBeenCalledWith('foo')
})
```

### 3.2 .toHaveBeenCalledWith() on React component mocks

Now let's use `.toHaveBeenCalledWith()` to test our `ChildComponent` mock. We expect it gets called with: `{ message: 'Hello' }`

```javascript
// part2/example2/__tests__/test1.js
import { render } from '@testing-library/react'
import ParentComponent from '../ParentComponent'
import ChildComponent from '../ChildComponent'

jest.mock('../ChildComponent')

// fails
test('The mocked ChildComponent gets called with the correct props', () => {
  render(<ParentComponent />)
  expect(ChildComponent).toHaveBeenCalledWith(
    { message: 'Hello' }
  )
})
```

But, this test fails. The reason for this is a bit obscure. When a `React` component gets called it actually receives two arguments: an object with the props and a ref. I don't fully understand this ref myself but just know that:
1. This ref value is usually empty (`{}`)
2. You need it or else your test fails.

So, let's update the test:

```javascript
// part2/example2/__tests__/test1.js
import { render } from '@testing-library/react'
import ParentComponent from '../ParentComponent'
import ChildComponent from '../ChildComponent'

jest.mock('../ChildComponent')

// passes
test('The mocked ChildComponent gets called with the { message: "Hello" } and {}', () => {
  render(<ParentComponent />)
  expect(ChildComponent).toHaveBeenCalledWith(
    { message: 'Hello' },
    {}
  )
})
```

Let me quickly recap. We are testing a mock of a `React` component: `<ChildComponent message="Hello" />`. We want to test if this mock was called with certain props. But, when a `React` component gets called it actually receives two arguments: An object with it's props and a second ref value. This second value is usually empty. 

The matcher `.toHaveBeenCalledWith()` **receives** two things from the mock: the props object and the ref value. We now pass into the matcher what we **expect** to find: an object with properties (to match the props) and an empty object ´{}´ to match the ref.

This test works but it isn't optimal. What if this ref does have a value for example. To counter this, we replace `{}` with the following line: `expect.anything()`.

[`.anything()`](https://jestjs.io/docs/expect#expectanything) is a `Jest` matcher that passes for anything except `undefined` or `null`. So, it's an ideal candidate to replace `{}` or an actual ref value. We update the test:

```javascript
  expect(ChildComponent).toHaveBeenCalledWith(
    { message: 'Hello' },
    expect.anything()
  )
```
A second improvement we can make is in the line `{ message: 'Hello'}}`. This performs an exact match. The received object has to exactly match the expected object we pass in. But, this is a not ideal. Let's say that for some reason we only wanted to test one property. How would we do this?

```javascript
  expect(ChildComponent).toHaveBeenCalledWith(
    expect.objectContaining({ 
      message: 'Hello',
    }),
    expect.anything()
  )
```

[`expect.objectContaining()`](https://jestjs.io/docs/expect#expectobjectcontainingobject) is another `Jest` matcher method. It compares a **received** object to an **expected** object. It will require that each property and value in the expected object is present in the received object. But, it does not work the other way.

```
// pass
received: { prop1: true, prop2: true }
expected: { prop1: true }

// fail
received: { prop1: true }
expected: { prop1: true, prop2: true }
```
So, using `expect.objectContaining()` makes our test more flexible. It allows us to choose what properties we test. Here is the full updated test:

```javascript
// part2/example2/__tests__/test1.js
test('The mocked ChildComponent gets called with the correct props', () => {
  render(<ParentComponent />)
  expect(ChildComponent).toHaveBeenCalledWith(
    expect.objectContaining({
      message: 'Hello',
    }),
    expect.anything()
  )
})
```

 We will go over the test once more. We are testing if the mock of the child component got called with the correct props, so we use the `.toHaveBeenCalled()` method on the mock `ChildComponent`. We receive two values: an object with the props and a ref value. We match the props object with `expect.objectContaining` because this gives us the flexibility to choose what props to test. For the second value, the ref, we use `expect.anything()`. We have to match the second value but we don't care about it. `expect.anything()` is the ideal solution.

## Summary

In this article we looked at why and how to set up an (automatic) mock. We then saw how to test these mocks and how to use `.toHaveBeenCalledWith()`.

In the [next part](https://dev.to/peterlidee/returning-values-from-mocks-jest-mocking-react-part-3-3lfn) in this series we show why and how we return values from mocks.