How To Test an AsyncStorage Insert in React Native
In a React Native app async storage any developer would eventually encounter this issue: testing a function that saves data to @react-native-async-storage/async-storage
. The library itself doesn’t need to be tested; you want to test that your code calls the library correctly with the right data.
A typical pattern is to have a service or a wrapper that encapsulates your storage logic. Let’s say you have a method like this that takes an object does things with it and finally saves it to storage:
// DiveSiteStore.ts
async saveDiveSite(diveSite: DiveSite): Promise<string | null> {
const diveSites = await this.getDiveSites();
let id: string | null = null;
// The key logic: if it's a new dive site, generate an ID
if (!diveSite.id) {
id = this.generateId(); // This creates a unique string identifier
const newDiveSite: DiveSite = {
...diveSite,
id
};
diveSites.push(newDiveSite);
} else {
id = diveSite.id
// ... handle updates ...
}
// This is the line we want to test
await AsyncStorage.setItem(DIVES_STORAGE_KEY, JSON.stringify(diveSites));
return id;
}
The main goal of our test is simple: when we call saveDiveSite with a new object, does it call AsyncStorage.setItem with a correctly formatted string that includes our new data and a generated ID?
Here’s how to do it step-by-step.
Step 1: Set Up the Mock
First, ensure you’ve mocked AsyncStorage globally, usually in a setupFilesAfterEnv
file like jest.setup.js
.
// jest.setup.js
import mockAsyncStorage from '@react-native-async-storage/async-storage/jest/async-storage-mock';
jest.mock('@react-native-async-storage/async-storage', () => mockAsyncStorage);
This replaces the real module with a Jest mock function, allowing us to spy on its methods.
Step 2: Write the Test
Now, in your test file, you can write a focused test for the insert functionality.
// DiveSiteStore.test.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import { DiveSiteStore } from './DiveSiteStore';
// Mock your ID generator if necessary
jest.mock('./idGenerator', () => ({
generateId: () => 'mock-generated-id-123'
}));
describe('DiveSiteStore - saveDiveSite (insert)', () => {
let store: DiveSiteStore;
beforeEach(() => {
store = new DiveSiteStore();
// Clear mock calls before each test
(AsyncStorage.setItem as jest.Mock).mockClear();
});
it('should call AsyncStorage.setItem with the new item containing a generated ID', async () => {
// 1. Arrange: Create a new dive site object without an ID
const newDiveSite = {
id: null, // This signifies a new item
name: 'Great Barrier Reef',
// all those other meaningful propertie go here
};
// 2. Act: Call the method under test
const returnedId = await store.saveDiveSite(newDiveSite);
// 3. Assert: Verify the interaction with AsyncStorage
// Check that setItem was called at all
expect(AsyncStorage.setItem).toHaveBeenCalledTimes(1);
// Get the arguments of the first call to setItem
const mockSetItemCalls = (AsyncStorage.setItem as jest.Mock).mock.calls;
const [calledKey, calledValue] = mockSetItemCalls[0];
// Check it was called with the correct key
expect(calledKey).toBe(DIVE_SITE_STORAGE_KEY);
// Parse the value it was called with (a JSON string)
const savedData = JSON.parse(calledValue);
// Find the new object in the array that was saved
expect(savedData).toContainEqual(
expect.objectContaining({
...newDiveSite,
id: returnedId // Verify ID value
})
);
expect(savedNewDiveSite.data.properties.id).toBe(returnedId); // ID was generated
});
// 4. Assert: Verify additional logic with the method under test
// ...
});
Why Doing It This Way
What I like about this approach is that it doesn’t test the mock storage itself, but rather verifies that my method is having the right conversation with AsyncStorage. I’m checking: “Did you tell AsyncStorage to save exactly what I expected you to save?”
It spies on the dependency (AsyncStorage.setItem).
It triggers your logic.
It intercepts and analyzes the inputs (the arguments) of that dependency call.
I think this is a pretty clean way to test storage wrappers. How would you test this scenario? Would you mock things differently? I’d love to hear your thoughts in the comments.