typescript - Jest testlerimde Zustand persist'i nasıl ele alabilirim?


Şu anda durum yönetimim için Zustand kullanan bir React Native uygulaması oluşturuyorum. Zustand, durumu depolamaya kaydetme yeteneğine sahip, benim durumumda React Native'in async storage'u. Sorunsuz çalışıyor, ancak testleri çalıştırırken Zustand'ın persist wrapper'ı jest test setimin başarısız olmasına neden oluyor.
Zustand durumumdan basit bir snippet:

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

Benim Testim:

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

Eğer persist sarmalayıcısını yorum satırı haline getirirsem, test geçiyor, ancak bu durumda hata mesajını alıyorum

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

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

https://docs.pmnd.rs/zustand/guides/testing kullanarak zustand'ın tamamını taklit etmeye çalıştım, ama bağlama hatası sadece taklit kısmına taşınıyor. Bağlamaya izin vermeyen şeyin store'un kendisinin undefined olup olmadığından ya da sadece persist'ten kaynaklanıp kaynaklanmadığından emin değilim. Ancak bunu nasıl ele alacağımı bilmiyorum. Bunu nasıl çözebileceğine dair fikirleri olan var mı?

Cevap #1

Yani ben benzer bir sorunu çözmeye çalışarak iki gün geçirdim, umarım bu da senin için işe yarar.

Fonksiyonları doğru şekilde dışa aktarmak için bazı ayarlamalar yapmam gerekse de, Zustand testlerini belgelerdeki gibi kurduğunuzdan emin olun.

Ana düzeltme, sıfırlama fonksiyonunu çağırdığımızda görülüyor, başlangıç durumunun derin bir kopyasını oluşturmam gerekiyordu. Daha sonra Zustand durumunu ayarlarken onun da başka bir derin kopyasını oluşturmak gerekiyor. Son derece garip ama işe yarıyor.

Referans olarak kalıcı depolama için @react-native-async-storage/async-storage kullanıyoruz. Bunun bunu etkiliyor olma ihtimali var.

Ayrıca node modüllerini taklit etmeyi unutmayın, dosya __mocks__ klasöründe olmalı ve adı aynı olmalı, yani 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()
    })
  });