2

I'm currently creating a react native app using Zustand for my state management. Zustand has the ability to persist the state into storage, in my case the async storage of react native. It works fine, however when running tests the persist wrapper of Zustand causes my jest suite to fail.
Simple snippet of my Zustand state:

  persist(
    (set) => ({
      variable: "something",
      changeVariable: (variable: string) => {
        set({ variable });
      }, 
    }),
    {
      name: "async-storage",
      getStorage: () => AsyncStorage,
    }
  )

My Test:

describe("<SomeComponent />", () => {
  it("renders default elements", () => {
    const { getByText } = render(<SomeComponent />);
    expect(getByText("Hello World")).toBeInTheDocument();
  });
});

If I comment out the persist wrapper, the test truns through, in this state however I get the error message

 TypeError: Cannot read properties of undefined (reading 'bind')

      14 | }
      15 |
    > 16 | export const useStore = create<ZustandState>()(
         |                                                        ^
      17 |     persist(
      18 |       (set) => ({

I tried to mock zustand as a whole using https://docs.pmnd.rs/zustand/guides/testing, but the binding error just moves to the mock then. I'm not sure if the store itself in undefined not allowing binding or if its just the persist itself. I however don't know how to tackle this. Anyone has ideas on how to counter this?

Yukko
  • 23
  • 5

1 Answers1

0

so I spent a two days trying to figure out a similar issue so I hope this works for you as well.

Ensure you setup the Zustand tests similar to the documentation although I had to make some adjustments in order to export the functions correctly.

The main fix is seen when we call the reset function, I had to create a deepclone of the initial state. Then create another deepclone of that when setting state of Zustand. Extremely odd but it works.

for reference we are using @react-native-async-storage/async-storage as our persistent storage. The a possibility this is affecting it.

Also dont forget to mock node modules the file must be in __mocks__ folder and named the same so zustand.ts

const { createStore: createStore } = jest.requireActual('zustand') as any;
export const storeResetFns = new Set<() => void>();

export const create = <S>(createState: StateCreator<S>) => {
  return typeof createState === 'function' ? createInternalFn(createState) : createInternalFn;
};

export default create;

const createInternalFn = <S>(createState: StateCreator<S>) => {
  const store = createStore(createState);
  const initialState = _.cloneDeep(store.getState());

  storeResetFns.add(() => {
    /**
      This is a hack to get around persisting 
      This might looks odd, it is. if you pass {initial state} to setState, then initial state gets updates to the current state...
    */
    const t = _.cloneDeep(initialState)
    if(initialState.trips) store.setState(t,true);
  })
  return store;
};

afterEach(() => {
    //clear out state before each test
    storeResetFns.forEach(fn => {
      fn()
    })
  });