Mocking Fetch API Calls When Using Jest
Tags: reactnative, testing, react, mobiledevelopment
Published: 2019-08-07 16:16:02

---
title: 'Mocking Fetch API Calls When Using Jest'
description: "In this lesson we'll learn how to mock fetch in your Jest tests."
published: true
tags: React Native, Testing, React, Mobile Development
canonical_url: https://www.reactnativeschool.com/mocking-fetch-api-calls-when-using-jest
---

> This tutorial was originally published on [React Native School](https://www.reactnativeschool.com/). For more tutorials on using React Native be sure to visit!

In this lesson we're going to make a few assumptions

- You're using [Jest](https://jestjs.io/en/) as your test runner
- You're familiar with the fetch API.

## Background Info

We're building an app that makes requests against the `https://jsonplaceholder.typicode.com` API but we don't want to actually make requests to that API every time we run our tests. That means we need to mock the `fetch` request and substitute a response.

A few things about the API:

- If fetching a list of data (`/posts`) the response will be an array
- If fetching a single item (`/posts/1`) the response will be an object with data
- If making an invalid request we'll get back an empty object

## Configuring the Testing Framework

To install jest run `yarn add --dev jest` (if you're using Expo you could alternatively use `jest-expo`).

Then, in your `package.json`, you'll want to configure jest by adding the following.

<p class="code-header">package.json</p>

```json
{
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "preset": "react-native" // alternatively use jest-expo if using expo
  }
}
```

You can then run `yarn test` to run tests. It won't do anything because you don't have any tests, yet.

Next, we need to setup our mocking for `fetch`. First `yarn add --dev jest-fetch-mock`.

Then in `package.json`:

<p class="code-header">package.json</p>

```json
{
  "jest": {
    "preset": "react-native", // alternatively use jest-expo if using expo
    "automock": false,
    "setupFiles": ["./setupJest.js"]
  }
}
```

We then need to create a `setupJest.js` file in the root of our project.

<p class="code-header">setupJest.js</p>

```jsx
global.fetch = require('jest-fetch-mock');
```

In it we just directly override the `global.fetch` function, which is what our app leverages to make remote requests.

## Starter Code

With Jest already installed, let's start an API function:

<p class="code-header">App/util/api.js</p>

```jsx
export const api = (path, options = {}) => {
  return fetch(`https://jsonplaceholder.typicode.com${path}`, options).then(
    res => res.json()
  );
};
```

This function will all a screen to simply call `api("/posts")` and it will then make the full request and parse the response into a JSON object.

## Requirements

The function above isn't quite done. It should:

- Return the result if it's an array
- Return the result if it's a non-empty object
- Throw an error if the result is an empty object

We can use that information to go ahead and right our tests.

## Writing the Tests

Following Jest conventions, we'll create a `__tests__/` directory in `util/` and put our tests there.

<p class="code-header">App/util/__tests__/api.test.js</p>

```jsx
import { api } from '../api';

beforeEach(() => {
  fetch.resetMocks();
});

test('returns result if array', () => {
  fetch.mockResponseOnce(JSON.stringify([{ id: 1 }]));
});
```

I add a `beforeEach` block which will run before each test in the file is run. In it we reset the fetch mock so that previous tests don't interfere with the test that's currently being run.

Then within the test we actually tell fetch what we want it to return - a stringified array.

<p class="code-header">App/util/__tests__/api.test.js</p>

```jsx
// ...

test('returns result if array', () => {
  fetch.mockResponseOnce(JSON.stringify([{ id: 1 }]));
  const onResponse = jest.fn();
  const onError = jest.fn();

  return api('/posts')
    .then(onResponse)
    .catch(onError)
    .finally(() => {
      expect(onResponse).toHaveBeenCalled();
      expect(onError).not.toHaveBeenCalled();

      expect(onResponse.mock.calls[0][0][0]).toEqual({ id: 1 });
    });
});
```

The actual test will make a request and, leveraging our functions promises and the use of jest mock functions, we can check that the right functions have been called in this test.

Finally we actually check the result of the test. Since we're using mock functions we can check how many times it has been called and what was passed to the function when it was called.

If you run the tests now you'll see that our test passes.

Next, we'll check for a non-empty object using the exact same process.

<p class="code-header">App/util/__tests__/api.test.js</p>

```jsx
// ...

test('returns result if non-empty object', () => {
  fetch.mockResponseOnce(JSON.stringify({ id: 1 }));
  const onResponse = jest.fn();
  const onError = jest.fn();

  return api('/posts')
    .then(onResponse)
    .catch(onError)
    .finally(() => {
      expect(onResponse).toHaveBeenCalled();
      expect(onError).not.toHaveBeenCalled();

      expect(onResponse.mock.calls[0][0]).toEqual({ id: 1 });
    });
});
```

Again, the test should pass.

Finally, we'll write our test for the final case.

<p class="code-header">App/util/__tests__/api.test.js</p>

```jsx
// ...

test('throws an error if empty object', () => {
  fetch.mockResponseOnce(JSON.stringify({}));
  const onResponse = jest.fn();
  const onError = jest.fn();

  return api('/posts')
    .then(onResponse)
    .catch(onError)
    .finally(() => {
      expect(onResponse).not.toHaveBeenCalled();
      expect(onError).toHaveBeenCalled();
    });
});
```

This time we switch things up - we check that `onResponse` is _not_ called and `onError` is called. If you run this test you'll see that it fails.

To fix the error we need to analyze the response before returning it from our API.

<p class="code-header">App/util/api.js</p>

```jsx
export const api = (path, options = {}) => {
  return fetch(`https://jsonplaceholder.typicode.com${path}`, options)
    .then(res => res.json())
    .then(response => {
      if (!Array.isArray(response) && Object.keys(response).length === 0) {
        throw new Error('Empty Response');
      }

      return response;
    });
};
```

You can see now that I check if the object is _not_ an array and check how many keys are on it. If it's not an array and it has no keys on the object, I throw an error.

Now if you run the tests you'll see we're all green!

## Additional Resources

- [jest-fetch-mock](https://github.com/jefflau/jest-fetch-mock#readme)
- [Jest](https://jestjs.io/en/)
- [Example with the above code](https://github.com/ReactNativeSchool/testing-example)