10

I'm trying to test my LoginScreen with Jest and Typescript. I use redux and redux-persist for storage and have set the storage up to use AsyncStorage as part of the config. I suspect that redux-persist is attempting to rehydrate after the built-in time-out function it uses runs out and tries to set storage to default storage? I'm getting the following error:

console.error
redux-persist: rehydrate for "root" called after timeout. undefined
undefined
at _rehydrate (node_modules/redux-persist/lib/persistReducer.js:70:71)
at node_modules/redux-persist/lib/persistReducer.js:102:11
at tryCallOne (node_modules/promise/setimmediate/core.js:37:12)
at Immediate._onImmediate (node_modules/promise/setimmediate/core.js:123:15)

Currently my test looks like this:

describe('Testing LoginScreen', () => {
  it('should render correctly', async () => {
    const { toJSON } = render(<MockedNavigator component={LoginScreen} />);
    await act(async () => await flushMicrotasksQueue());
    expect(toJSON()).toMatchSnapshot();
  });
});

and my MockNavigator looks like this:

type MockedNavigatorProps = {
  component: React.ComponentType<any>;
  params?: {};
};

const Stack = createStackNavigator();
const MockedNavigator = (props: MockedNavigatorProps) => {
  return (
    <MockedStorage>
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen
            name='MockedScreen'
            component={props.component}
            initialParams={props.params}
          />
        </Stack.Navigator>
      </NavigationContainer>
    </MockedStorage>
  );
};

export default MockedNavigator;

Here is the way I'm creating my storage:

import 'react-native-gesture-handler';
import * as React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from '../src/AppState/store';

type MockedStorageProps = {
  children: any;
};

const MockedStorage = (props: MockedStorageProps) => {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        {props.children}
      </PersistGate>
    </Provider>
  );
};

export default MockedStorage;
skyboyer
  • 22,209
  • 7
  • 57
  • 64
Mosef
  • 217
  • 4
  • 11

2 Answers2

18

I resolved this same error using this advice from an issue on the redux-persist repo: https://github.com/rt2zz/redux-persist/issues/1243#issuecomment-692609748.

(It also had the side-effect of avoiding logging errors in test from redux-logger.)

jest.mock('redux-persist', () => {
  const real = jest.requireActual('redux-persist');
  return {
    ...real,
    persistReducer: jest
      .fn()
      .mockImplementation((config, reducers) => reducers),
  };
});

@alexbrazier:

It basically just bypasses redux-persist by returning the reducers directly without wrapping them in redux-persist.

LordParsley
  • 3,808
  • 2
  • 30
  • 41
  • 5
    Applying this worked but I ended up with a new issue where all my renders were null. That was because of the null passed into the PersistGate which can be fixed in the tests by adding `jest.mock('redux-persist/integration/react', () => ({ PersistGate: (props: any) => props.children, }));` With both of these in place my tests are running fine. – Mosef Jan 25 '21 at 18:18
0

LordParsley's solution didn't work for me. (caused tests to timeout), but put me on the right path. Adding this to my jest.setup.js resolved my error:

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

Solution came from this discussion: https://github.com/react-native-async-storage/async-storage/issues/379, they also suggest copying the mocks, but that didn't work for me.

UPDATE: a colleague didn't like the solution, preferring instead this, so that you only mock the function that causes the error and leave all the other errors as in the original mock, add the following to the mock file for async storage, not the jest.setup and refer to it in your jest.config:

import asyncStorageMock from '@react-native-async-storage/async-storage/jest/async-storage-mock';

const originalSetItemMock = asyncStorageMock.setItem.bind(asyncStorageMock);
asyncStorageMock.setItem = function newSetItemMock() {
    // Types falsely assert that the below mock function returns a promise, so we ignore the return value.
    originalSetItemMock(...arguments);
    return Promise.resolve();
};
export default asyncStorageMock;
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Mister_CK
  • 668
  • 3
  • 13