question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Jest] Mocked async storage

See original GitHub issue

Current behavior

Hey guys ! I tried to mock async storage by applying what is written in the “jest integration” section. i’m getting Cannot read property 'getItem' of undefined when running tests. The app works without any problem when launched, the issue only appears when running tests. I am not testing directly async storage in my tests, I am testing a component that uses AsyncStorage. The test:

  it('LoginDeleteAccount renders correctly', () => {
    const { toJSON } = render(<MockedNavigator component={LoginDeleteAccount} />)
    expect(toJSON()).toMatchSnapshot()
  })

Inside the component LoginDeleteAccount:

  async retrieveEmails() {
    try {
      const emails_array = await AsyncStorage.getItem('emails')
      ...
    } catch (error) {
      console.log(error)
    }
  }

As for the mocking, I have a index.js file in __mocks__/@react-native-community/async-storage. In it: export default from '@react-native-community/async-storage/jest/async-storage-mock'

Any ideas ? thanks in advance !

Expected behavior

Test runs without any errors

Repro steps

  • npm test

Environment

  • Async Storage version: 1.11.0
  • React-Native version: 0.62.2
  • Platform tested: Jest(terminal)/iOS / Android
  • Logs/Error that are relevant: error: TypeError: Cannot read property 'getItem' of undefined

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:22 (5 by maintainers)

github_iconTop GitHub Comments

27reactions
karlmarxlopezcommented, Jul 18, 2020

I temporarily solved cannot read .catch of undefined when using redux-persist.

Instead of using the ready-made mocks, I manually added this to my jest.setup.ts

jest.mock('@react-native-community/async-storage', () => {
  return {
    getItem: async (...args) => args,
    setItem: async (...args) => args,
    removeItem: async (...args) => args,
  };
});

Or, better. Copy and paste this to your project’s __mocks__(create this directory if it does not exist)

__mocks__/@react-native-community/async-storage/index.js

const asMock = {
  __INTERNAL_MOCK_STORAGE__: {},

  setItem: async (key, value, callback) => {
    const setResult = await asMock.multiSet([[key, value]], undefined);

    callback && callback(setResult);
    return setResult;
  },

  getItem: async (key, callback) => {
    const getResult = await asMock.multiGet([key], undefined);

    const result = getResult[0] ? getResult[0][1] : null;

    callback && callback(null, result);
    return result;
  },

  removeItem: (key, callback) => asMock.multiRemove([key], callback),
  mergeItem: (key, value, callback) =>
    asMock.multiMerge([[key, value]], callback),

  clear: _clear,
  getAllKeys: _getAllKeys,
  flushGetRequests: () => {},

  multiGet: _multiGet,
  multiSet: _multiSet,
  multiRemove: _multiRemove,
  multiMerge: _multiMerge,
};

async function _multiSet(keyValuePairs, callback) {
  keyValuePairs.forEach((keyValue) => {
    const key = keyValue[0];

    asMock.__INTERNAL_MOCK_STORAGE__[key] = keyValue[1];
  });
  callback && callback(null);
  return null;
}

async function _multiGet(keys, callback) {
  const values = keys.map((key) => [
    key,
    asMock.__INTERNAL_MOCK_STORAGE__[key] || null,
  ]);
  callback && callback(null, values);

  return values;
}

async function _multiRemove(keys, callback) {
  keys.forEach((key) => {
    if (asMock.__INTERNAL_MOCK_STORAGE__[key]) {
      delete asMock.__INTERNAL_MOCK_STORAGE__[key];
    }
  });

  callback && callback(null);
  return null;
}

async function _clear(callback) {
  asMock.__INTERNAL_MOCK_STORAGE__ = {};

  callback && callback(null);

  return null;
}

async function _getAllKeys() {
  return Object.keys(asMock.__INTERNAL_MOCK_STORAGE__);
}

async function _multiMerge(keyValuePairs, callback) {
  keyValuePairs.forEach((keyValue) => {
    const key = keyValue[0];
    const value = JSON.parse(keyValue[1]);

    const oldValue = JSON.parse(asMock.__INTERNAL_MOCK_STORAGE__[key]);

    asMock.__INTERNAL_MOCK_STORAGE__[key] = JSON.stringify(
      _deepMergeInto(oldValue, value),
    );
  });

  callback && callback(null);
  return null;
}

const _isObject = (obj) => typeof obj === 'object' && !Array.isArray(obj);
const _deepMergeInto = (oldObject, newObject) => {
  const newKeys = Object.keys(newObject);
  const mergedObject = oldObject;

  newKeys.forEach((key) => {
    const oldValue = mergedObject[key];
    const newValue = newObject[key];

    if (_isObject(oldValue) && _isObject(newValue)) {
      mergedObject[key] = _deepMergeInto(oldValue, newValue);
    } else {
      mergedObject[key] = newValue;
    }
  });

  return mergedObject;
};

export default asMock;

I don’t know why wrapping the async functions with jest.fn(async () => {}) does not work.

8reactions
MartinCerny-awincommented, Jun 12, 2020

I think I am having the same issue when using AsyncStorage through redux-persist.

/Volumes/sites/clim8/mobile-app/node_modules/redux-persist/lib/createPersistoid.js:98
    writePromise = storage.setItem(storageKey, serialize(stagedState)).catch(onWriteFail);
                                                                      ^

TypeError: Cannot read property 'catch' of undefined
Read more comments on GitHub >

github_iconTop Results From Across the Web

Jest integration | Async Storage - GitHub Pages
Jest integration. Async Storage module is tightly coupled with its NativeModule part - it needs a running React Native application to work properly....
Read more >
How to test Async Storage with Jest?
Inside that file, export Async Storage mock. ... Jest should then mock AsyncStorage by default in all your tests. If it doesn't, try...
Read more >
mock-async-storage
Its a mock of react-native AsyncStorage for jest tests. Latest version: 2.2.0, last published: 3 years ago. Start using mock-async-storage ...
Read more >
Jest integration - invertase/react-native-async-storage
Using Async Storage mock. You can use one of two ways to provide mocked version of AsyncStorage : With mocks directory. In ...
Read more >
mock-async-storage
Jest Mock AsyncStorage for react-native. Travis CI Build Status. Standard - JavaScript Style Guide. Its a mock of react-native AsyncStorage for jest tests ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found