Technically Tia
An iphone on a colourful background
4 February 2023
React Native
Testing

How to Mock and Test AsyncStorage in React Native

Since the original React Native AsyncStorage was deprecated, you may be using the community solution: react-native-async-storage. I would personally recommend it!

Once you start writing your unit tests with Jest & React Native Testing Library, you'll notice that initially you will get an error about async storage when we run the tests. Something like this:

Test suite failed to run [@RNC/AsyncStorage]: NativeModule: AsyncStorage is null.

OK! All you need to do is to mock AsyncStorage...

If you don't already have a test setup file, then create one and add the following jest mock:

// setupTests.js jest.mock("@react-native-async-storage/async-storage", () => require("@react-native-async-storage/async-storage/jest/async-storage-mock"), );

This mock comes from the react-native-async-storage library and you can read more about it here on their docs.

Now make sure you reference you setupTest.js file in your package.json like this, so that it actually gets used:

"jest": { "preset": "jest-expo", "setupFiles": ["./setupTests.js"], "transformIgnorePatterns": [ "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)|@react-native-async-storage/async-storage" ] },

Now the previous error should be gone and you can now test the behaviour of your AsyncStorage actions!

For example, you might have a todo list app that stores new todo items in AsyncStorage, then whenever you load up the app it gets all the items from AsyncStorage and displays them. Here is an example of a test covering the following scenario:

import React from "react"; import { render, waitFor, screen, fireEvent, } from "@testing-library/react-native"; import App from "../App.tsx"; import AsyncStorage from "@react-native-async-storage/async-storage"; import renderer from "react-test-renderer"; jest.useFakeTimers(); const itemData = [ { key: "1", text: "test item" }, { key: "2", text: "another" }, { key: "3", text: "and another" }, ]; describe("<App />", () => { beforeEach(async () => { const jsonValue1 = JSON.stringify(itemData[0]); const jsonValue2 = JSON.stringify(itemData[1]); const jsonValue3 = JSON.stringify(itemData[2]); await AsyncStorage.setItem(itemData[0].key, jsonValue1); await AsyncStorage.setItem(itemData[1].key, jsonValue2); await AsyncStorage.setItem(itemData[2].key, jsonValue3); waitFor(async () => { expect(AsyncStorage.getAllKeys).toHaveBeenCalled(); }); waitFor(async () => { expect(await AsyncStorage.getAllKeys()).toEqual(["1", "2", "3"]); }); }); it("getAllKeys is called on render and items from async storage are displayed", async () => { render(<App />); const item1 = await screen.findByText("test item"); const item2 = await screen.findByText("another"); const item3 = await screen.findByText("and another"); const falseItem = screen.queryByText("i shouldn't exist"); expect(item1).toBeTruthy(); expect(item2).toBeTruthy(); expect(item3).toBeTruthy(); expect(falseItem).toBeFalsy(); }); it("new todo item is rendered to the displayed list when added", async () => { render(<App />); const input = screen.getByPlaceholderText("new todo..."); const addTodoBtn = screen.getByText("ADD TODO"); fireEvent(input, "onChangeText", "New Task"); fireEvent(addTodoBtn, "press"); waitFor(async () => { expect(await screen.findByText("New Task")).toBeTruthy(); }); }); });

I have imported AsyncStorage at the top of the file and included a beforeEach that adds my mock data to Async Storage before the tests. Now I can interact with it, just like I would in the real app.

You can also see the full repo for the todo list project here - credit also goes to the Net Ninja for teaching me how to create a todo list app within his tutorial here.

If you have any tips or suggestions of your own, then feel free to comment!

Happy testing!