乐闻世界logo
搜索文章和话题

Zustand 中级面试题:如何对 Zustand store 进行单元测试?

3月7日 11:44

对 Zustand store 进行单元测试相对简单,因为 store 是纯 JavaScript 对象。

基本测试示例:

javascript
// store.js import { create } from 'zustand'; const useStore = create((set) => ({ count: 0, user: null, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), setUser: (user) => set({ user }), reset: () => set({ count: 0, user: null }) })); export default useStore;
javascript
// store.test.js import { renderHook, act } from '@testing-library/react'; import useStore from './store'; describe('Zustand Store', () => { beforeEach(() => { // 每个测试前重置 store useStore.setState({ count: 0, user: null }); }); test('should initialize with default values', () => { const { result } = renderHook(() => useStore()); expect(result.current.count).toBe(0); expect(result.current.user).toBeNull(); }); test('should increment count', () => { const { result } = renderHook(() => useStore()); act(() => { result.current.increment(); }); expect(result.current.count).toBe(1); }); test('should decrement count', () => { const { result } = renderHook(() => useStore()); act(() => { result.current.decrement(); }); expect(result.current.count).toBe(-1); }); test('should set user', () => { const { result } = renderHook(() => useStore()); const mockUser = { id: 1, name: 'John' }; act(() => { result.current.setUser(mockUser); }); expect(result.current.user).toEqual(mockUser); }); test('should reset store', () => { const { result } = renderHook(() => useStore()); const mockUser = { id: 1, name: 'John' }; act(() => { result.current.setUser(mockUser); result.current.increment(); }); expect(result.current.count).toBe(1); expect(result.current.user).toEqual(mockUser); act(() => { result.current.reset(); }); expect(result.current.count).toBe(0); expect(result.current.user).toBeNull(); }); });

测试异步操作:

javascript
// store.js const useStore = create((set) => ({ user: null, loading: false, error: null, fetchUser: async (userId) => { try { set({ loading: true, error: null }); const response = await fetch(`/api/users/${userId}`); const userData = await response.json(); set({ user: userData, loading: false }); } catch (err) { set({ error: err.message, loading: false }); } } }));
javascript
// store.test.js import { renderHook, act, waitFor } from '@testing-library/react'; import useStore from './store'; describe('Zustand Store - Async Operations', () => { beforeEach(() => { useStore.setState({ user: null, loading: false, error: null }); }); test('should fetch user successfully', async () => { global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ id: 1, name: 'John' }) }) ); const { result } = renderHook(() => useStore()); await act(async () => { await result.current.fetchUser(1); }); expect(result.current.user).toEqual({ id: 1, name: 'John' }); expect(result.current.loading).toBe(false); expect(result.current.error).toBeNull(); }); test('should handle fetch error', async () => { global.fetch = jest.fn(() => Promise.reject(new Error('Network error'))); const { result } = renderHook(() => useStore()); await act(async () => { await result.current.fetchUser(1); }); expect(result.current.error).toBe('Network error'); expect(result.current.loading).toBe(false); }); });

测试选择性订阅:

javascript
import { renderHook } from '@testing-library/react'; import useStore from './store'; describe('Zustand Store - Selective Subscription', () => { beforeEach(() => { useStore.setState({ count: 0, user: null }); }); test('should only re-render when subscribed state changes', () => { const renderCount = jest.fn(); const { result } = renderHook(() => { renderCount(); return useStore((state) => state.count); }); expect(renderCount).toHaveBeenCalledTimes(1); act(() => { useStore.getState().setUser({ id: 1, name: 'John' }); }); // 不应该重新渲染,因为 user 变化,但订阅的是 count expect(renderCount).toHaveBeenCalledTimes(1); act(() => { useStore.getState().increment(); }); // 应该重新渲染,因为 count 变化 expect(renderCount).toHaveBeenCalledTimes(2); }); });

关键点:

  • 使用 @testing-library/reactrenderHookact 进行测试
  • 在每个测试前重置 store 状态
  • 对于异步操作,使用 waitFor 等待状态更新
  • 测试选择性订阅时,验证重渲染次数
  • 使用 useStore.getState() 直接访问和操作 store
标签:ReactZustand