I\'m building an app with React Native. I want to minimize how often I communicate to the database, so I make heavy use of AsyncStorage. There\'s a lot of room for bugs in t
For everyone who sees this question in > 2019:
Since February 2019, AsyncStorage was moved to @react-native-community/async-storage, which causes this warning to appear if you're importing it from react-native
:
Warning: Async Storage has been extracted from react-native core and will be removed in a future release.
The new module includes its own mock, so you don't have to worry about writing your own anymore.
Per the project's documentation, you can set it up in 2 different ways:
__mocks__/@react-native-community
directory.export default from '@react-native-community/async-storage/jest/async-storage-mock'
Jest should then mock AsyncStorage
by default in all your tests. If it doesn't, try calling jest.mock(@react-native-community/async-storage)
at the top of your test file.package.json
or jest.config.js
) add the setup file's location:
"jest": {
"setupFiles": ["./path/to/jestSetupFile.js"]
}
Inside your setup file, set up the AsyncStorage mock:
import mockAsyncStorage from '@react-native-community/async-storage/jest/async-storage-mock';
jest.mock('@react-native-community/async-storage', () => mockAsyncStorage);
If you're using TypeScript, using the 2nd option (Jest setup file) is way easier, since with the 1st one (mocks directory) it won't associate @types/react-native-community__async-storage
with the mock automatically.
Install the module using the command : Run this command from the root directory of the project.
npm install --save mock-async-storage
In the project root directory create __mocks__\@react-native-community
folder. Inside that create a file async-storage.js.
Code in async-storage.js
export default from '@react-native-community/async-storage/jest/async-storage-mock'
Inside package.json configure the jest as follows:
"jest": {
"preset": "jest-expo",
"transformIgnorePatterns" : ["/node_modules/@react-native-community/async-storage/(?!(lib))"]
},
Here I am using jest-expo for testing. If you are using jest then the preset value will be jest not jest-expo.
In the project root directory create a file called jest.config.js Configuration inside the jest.config.js file:
module.exports = {
setupFilesAfterEnv: [
'./setup-tests.js',
],
};
In the project root directory create a file called setup-tests.js. Code in this file is :
import MockAsyncStorage from 'mock-async-storage';
const mockImpl = new MockAsyncStorage();
jest.mock('@react-native-community/async-storage', () => mockImpl);
In the project root directory create the test file. Here I am calling it Example.test.js.
import AsyncStorage from '@react-native-community/async-storage';
beforeEach(() => {
AsyncStorage.clear();
// console.log(`After the data is being reset :`)
// console.log(AsyncStorage)
});
it('can read asyncstorage', async () => {
await AsyncStorage.setItem('username', 'testUser')
let usernameValue = await AsyncStorage.getItem('username')
// console.log(`After the data is being set :`)
// console.log(AsyncStorage)
expect(usernameValue).toBe('testUser')
});
Here setting the value of username to testUser using AsyncStorage.setItem. Then fetching the value using getItem function. The test case is to compare whether the usernameValue is equal to testUser. If yes then the test case passes else the test case will fail.
Using beforeEach so that every time a test case is being run Asyncstorage is being cleared and is empty. If needed one can check what is present in Asyncstorage using console.log
Run the command yarn test to run the tests. The output is:
For anyone else to get here, asyncStorage also needs a callback, some libs use this
My original answer just pointed at how the author of react-native-simple-store had dealt with the mocking. I've updated my answer with my own mocking that removes Jason's hard-coded mock responses.
Jason Merino has a nice simple approach to this in https://github.com/jasonmerino/react-native-simple-store/blob/master/tests/index-test.js#L31-L64
jest.mock('react-native', () => ({
AsyncStorage: {
setItem: jest.fn(() => {
return new Promise((resolve, reject) => {
resolve(null);
});
}),
multiSet: jest.fn(() => {
return new Promise((resolve, reject) => {
resolve(null);
});
}),
getItem: jest.fn(() => {
return new Promise((resolve, reject) => {
resolve(JSON.stringify(getTestData()));
});
}),
multiGet: jest.fn(() => {
return new Promise((resolve, reject) => {
resolve(multiGetTestData());
});
}),
removeItem: jest.fn(() => {
return new Promise((resolve, reject) => {
resolve(null);
});
}),
getAllKeys: jest.fn(() => {
return new Promise((resolve) => {
resolve(['one', 'two', 'three']);
});
})
}
}));
My own mock:
const items = {};
jest.mock('react-native', () => ({
AsyncStorage: {
setItem: jest.fn((item, value) => {
return new Promise((resolve, reject) => {
items[item] = value;
resolve(value);
});
}),
multiSet: jest.fn((item, value) => {
return new Promise((resolve, reject) => {
items[item] = value;
resolve(value);
});
}),
getItem: jest.fn((item, value) => {
return new Promise((resolve, reject) => {
resolve(items[item]);
});
}),
multiGet: jest.fn((item) => {
return new Promise((resolve, reject) => {
resolve(items[item]);
});
}),
removeItem: jest.fn((item) => {
return new Promise((resolve, reject) => {
resolve(delete items[item]);
});
}),
getAllKeys: jest.fn((items) => {
return new Promise((resolve) => {
resolve(items.keys());
});
})
}
}));
May be you can try something like this:
mockStorage.js
export default class MockStorage {
constructor(cache = {}) {
this.storageCache = cache;
}
setItem = jest.fn((key, value) => {
return new Promise((resolve, reject) => {
return (typeof key !== 'string' || typeof value !== 'string')
? reject(new Error('key and value must be string'))
: resolve(this.storageCache[key] = value);
});
});
getItem = jest.fn((key) => {
return new Promise((resolve) => {
return this.storageCache.hasOwnProperty(key)
? resolve(this.storageCache[key])
: resolve(null);
});
});
removeItem = jest.fn((key) => {
return new Promise((resolve, reject) => {
return this.storageCache.hasOwnProperty(key)
? resolve(delete this.storageCache[key])
: reject('No such key!');
});
});
clear = jest.fn((key) => {
return new Promise((resolve, reject) => resolve(this.storageCache = {}));
});
getAllKeys = jest.fn((key) => {
return new Promise((resolve, reject) => resolve(Object.keys(this.storageCache)));
});
}
and inside your test file:
import MockStorage from './MockStorage';
const storageCache = {};
const AsyncStorage = new MockStorage(storageCache);
jest.setMock('AsyncStorage', AsyncStorage)
// ... do things
I think jest.setMock could be in this case better than jest.mock so we can use react-native
with no problem just mocking the AsyncStorage
like that:
jest.setMock('AsyncStorage', {
getItem: jest.fn(
item =>
new Promise((resolve, reject) => {
resolve({ myMockObjectToReturn: 'myMockObjectToReturn' });
})
),
});