问题
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 the translation between DB and AsyncStorage though. Therefore, I want to make sure that AsyncStorage has the data I believe it does by running automated tests against it. Surprisingly, I haven't found any information on how to do that online. My attempts to do it on my own haven't worked out.
Using Jest:
it("can read asyncstorage", () => {
return AsyncStorage.getItem('foo').then(foo => {
expect(foo).not.toBe("");
}); });
This method failed with an error:
TypeError: RCTAsyncStorage.multiGet is not a function
Removing the return will cause it to run instantly without waiting for the value and improperly pass the test.
I got hit with the exact same error when I tried to test it using the await keyword:
it('can read asyncstorage', async () => {
this.foo = "";
await AsyncStorage.getItem('foo').then(foo => {
this.foo = foo;
});
expect(foo).not.toBe(""); });
Any suggestions on how to successfully run assertions against the values in AsyncStorage? I'd prefer to continue using Jest but if it can only be done with some alternate testing library I'm open to that.
回答1:
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());
});
})
}
}));
回答2:
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
回答3:
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:
With mocks directory
- In your project root directory, create a
__mocks__/@react-native-community
directory. - Inside that folder, create async-storage.js file.
- Inside that file, export Async Storage mock.
Jest should then mockexport default from '@react-native-community/async-storage/jest/async-storage-mock'
AsyncStorage
by default in all your tests. If it doesn't, try callingjest.mock(@react-native-community/async-storage)
at the top of your test file.
With Jest setup file
- In your Jest config (probably in
package.json
orjest.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.
回答4:
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' });
})
),
});
回答5:
For anyone else to get here, asyncStorage also needs a callback, some libs use this
来源:https://stackoverflow.com/questions/40952566/how-to-test-async-storage-with-jest