Testing multiple instances of the same mocked component
Tags: javascript, react, testing
Published: 2020-09-07 20:21:04

---
title: Testing multiple instances of the same mocked component
published: true
description: Mocks aren’t evil! They can help you build simpler, more resilient tests. Here’s a guide to using mocks well.
tags: javascript, react, testing 
series: Mocking React components with Testing Library
---

This is part four of a series on testing React with component mocks. In part 2 we looked at the basic form of component mocks. In part 3, we added the ability to assert on component children. Now we’ll look at the most complex piece of the puzzle: handling multiple instances of the same mock.

---

_All the code samples for this post are available at the following repo._

{% github dirv/mocking-react-components %}

---
Let’s continue with a new component, `TopFivePostsPage`, which perhaps unsurprisingly shows the top five posts.

```javascript
import { PostContent } from "./PostContent"

export const TopFivePostsPage = () => (
  <ol>
    <PostContent id="top1" />
    <PostContent id="top2" />
    <PostContent id="top3" />
    <PostContent id="top4" />
    <PostContent id="top5" />
  </ol>
);
```

To test that, we use `queryAllByTestId` in combination with the `toHaveLength` matcher.

```javascript
describe("BlogPage", () => {
  it("renders five PostContent components", () => {
    render(<TopFivePostsPage />)
    expect(screen.queryAllByTestId("PostContent"))
      .toHaveLength(5)
  })
})
```

And for our second test, we can use five `expect` statements, each with the different prop values.

```javascript
it("constructs a PostContent for each top 5 entry", () => {
  render(<TopFivePostsPage />)
  expect(PostContent).toHaveBeenCalledWith(
    { id: "top1" }, expect.anything())
  expect(PostContent).toHaveBeenCalledWith(
    { id: "top2" }, expect.anything())
  expect(PostContent).toHaveBeenCalledWith(
    { id: "top3" }, expect.anything())
  expect(PostContent).toHaveBeenCalledWith(
    { id: "top4" }, expect.anything())
  expect(PostContent).toHaveBeenCalledWith(
    { id: "top5" }, expect.anything())
})
```

But there’s something not quite right about this. We haven’t tested the _order_ of rendering. The `toHaveBeenCalledWith` matcher doesn’t care about order.

We can use `.mock.calls` instead.

```javascript
it("renders PostContent items in the right order", () => {
  render(<TopFivePostsPage />)
  const postContentIds = PostContent.mock.calls.map(
    args => args[0].id)

  expect(postContentIds).toEqual([
    "top1", "top2", "top3", "top4", "top5"
  ])
})
```

If you try running this after the first two tests for `TopFivePostsPage`, you’ll get a strange error that `PostContent` was actually called _fifteen_ times! That’s because when we need to _clear_ our mock between each test.

We do that by adding the `clearMocks` property to our Jest config. Here’s my `package.json` for comparison.

```json
"jest": {
  "transform": {
    "^.+\\.jsx?$": "babel-jest"
  },
  "setupFilesAfterEnv": ["./jest.setup.js"],
  "clearMocks": true
}
```

Notice the last test we wrote actually makes the previous test redundant, so you can delete that one safely.

##When that’s not enough: mock instance IDs

Very occasionally, you'll need more than this. For example, if you need to test children passed _and_ you also have multiple instances. In that case, you can use one of the component’s props to give a **unique** test ID to your component instance.

```javascript
jest.mock("../src/PostContent", () => ({
  PostContent: jest.fn(({ children, id }) => (
    <div data-testid={`PostContent-${id}`}>
      {children}
    </div>
  ))
}))
```

Personally, I really dislike this. It’s _complex_, and more complex than I’m comfortable with. But it exists, and sometimes it’s necessary to use it.

---

_Remember that mocks are there to help you speed up your testing, and testing is there to help speed up your development. When mocks become overly complex, you have to spend more time reading them and maintaining them, so they slow you down. I’ll cover more on this in the next part._

---

##Yet more lessons

So what have we learned now?

* Use `queryAllByTestId` when testing multiple instances of a mocked component
* Use `.mock.calls` to check ordering of calls, or for testing render props.
* Use Jest’s `clearMocks` configuration setting to ensure your spies are cleared before each test.
* If all else fails, you can use props within your rendered output to give unique `data-testid` values for each instance.
* Keep your mocks as simple as possible!

That’s all there is to it. In the final part, we’ll look at why mocks can get you into trouble—and how to avoid it.