You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在组件外调用mock createStore?Zustand测试场景答疑

问题描述

我已经按照文档配置了Vite mock文件,但在测试场景中无法正确使用。具体场景如下:

带中间件的Zustand Store

export const useStore = create<Store>()(
  devtools(
    subscribeWithSelector((set) => ({
      currentPage: 1,
      error: null,
      isLoading: false,
      photos: [],
      searchTerm: '',
      setIsLoading: (isLoading) => set({ isLoading }),
      setPhotos: (photos) => set({ photos }),
      setSearchTerm: (searchTerm) => set({ searchTerm }),
      setError: (error) => set({ error }),
    }))
  )
);

当前测试代码(使用真实Store)

test('renders when loading', () => {
  useStore.setState({ isLoading: true }); // 想用mock store替代此处
  const component = render(<ImageGrid />);
  expect(component.asFragment()).toMatchInlineSnapshot(`...`);
});

// 状态未重置会污染后续测试

当前核心问题:上述方式无法使用mock中定义的storeResetFns,如何用create函数创建mock store以在测试中轻松设置状态?


解决方案

1. 抽离Store创建逻辑,实现可替换

先把Store的核心创建逻辑单独封装,避免直接导出固定实例:

// store.ts
export const createStore = () => create<Store>()(
  devtools(
    subscribeWithSelector((set) => ({
      currentPage: 1,
      error: null,
      isLoading: false,
      photos: [],
      searchTerm: '',
      setIsLoading: (isLoading) => set({ isLoading }),
      setPhotos: (photos) => set({ photos }),
      setSearchTerm: (searchTerm) => set({ searchTerm }),
      setError: (error) => set({ error }),
    }))
  )
);

// 默认导出供业务代码使用
export const useStore = createStore();

2. 测试中创建独立Mock Store实例

每次测试前生成全新的Store实例,彻底避免状态污染,同时通过Provider让组件使用mock实例:

// ImageGrid.test.tsx
import { createStore } from './store';
import { render } from '@testing-library/react';
import ImageGrid from './ImageGrid';

let mockStore: ReturnType<typeof createStore>;

beforeEach(() => {
  // 每次测试初始化新的store
  mockStore = createStore();
  // 可在这里设置默认初始状态
  mockStore.setState({ isLoading: false, photos: [] });
});

test('renders when loading', () => {
  // 设置当前测试所需的状态
  mockStore.setState({ isLoading: true });
  
  // 用mock store的Provider包裹组件
  render(
    <mockStore.Provider>
      <ImageGrid />
    </mockStore.Provider>
  );
  
  expect(component.asFragment()).toMatchInlineSnapshot(`...`);
});

3. 集成storeResetFns实现一键重置

如果需要使用Zustand的storeResetFns,可以在创建Store时内置重置逻辑并注册:

// store.ts
import { storeResetFns } from 'zustand/middleware';

export const createStore = () => {
  const store = create<Store>()(
    devtools(
      subscribeWithSelector((set) => ({
        currentPage: 1,
        error: null,
        isLoading: false,
        photos: [],
        searchTerm: '',
        setIsLoading: (isLoading) => set({ isLoading }),
        setPhotos: (photos) => set({ photos }),
        setSearchTerm: (searchTerm) => set({ searchTerm }),
        setError: (error) => set({ error }),
        // 自定义重置方法
        reset: () => set({
          currentPage: 1,
          error: null,
          isLoading: false,
          photos: [],
          searchTerm: '',
        }),
      }))
    )
  );
  
  // 将重置方法注册到storeResetFns
  storeResetFns.add(() => store.getState().reset());
  
  return store;
};

在测试中可以直接调用重置:

beforeEach(() => {
  mockStore = createStore();
  // 一键重置到初始状态
  mockStore.getState().reset();
});

4. 封装辅助函数简化测试流程

可以写一个通用渲染函数,自动注入mock store并支持初始状态配置:

const renderWithMockStore = (ui: React.ReactElement, initialState?: Partial<Store>) => {
  const store = createStore();
  if (initialState) {
    store.setState(initialState);
  }
  return {
    ...render(<store.Provider>{ui}</store.Provider>),
    store,
  };
};

// 使用示例
test('renders when loading', () => {
  const { container } = renderWithMockStore(<ImageGrid />, { isLoading: true });
  expect(container).toMatchInlineSnapshot(`...`);
});

内容的提问来源于stack exchange,提问作者Simon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 21:23:15