Your test runner needs to be configured to run JavaScript/TypeScript syntax. For UI components, configure the test runner to use JSDOM for a mock DOM environment.
Jest
Industry-standard testing framework with great ecosystem support
Vitest
Fast, modern testing framework with ES modules support
We recommend using React Testing Library (RTL) to test React components that connect to Zustand. RTL encourages good testing practices and uses ReactDOMβs render and act utilities.
Jest uses CommonJS modules while Vitest uses ES modules. Keep this in mind when setting up your tests.
The mock below enables test runners to reset Zustand stores after each test.
Jest
Vitest
1
Create Zustand Mock
__mocks__/zustand.ts
import { act } from '@testing-library/react'import type * as ZustandExportedTypes from 'zustand'export * from 'zustand'const { create: actualCreate, createStore: actualCreateStore } = jest.requireActual<typeof ZustandExportedTypes>('zustand')// a variable to hold reset functions for all stores declared in the appexport const storeResetFns = new Set<() => void>()const createUncurried = <T>( stateCreator: ZustandExportedTypes.StateCreator<T>,) => { const store = actualCreate(stateCreator) const initialState = store.getInitialState() storeResetFns.add(() => { store.setState(initialState, true) }) return store}// when creating a store, we get its initial state, create a reset function and add it in the setexport const create = (<T>( stateCreator: ZustandExportedTypes.StateCreator<T>,) => { console.log('zustand create mock') // to support curried version of create return typeof stateCreator === 'function' ? createUncurried(stateCreator) : createUncurried}) as typeof ZustandExportedTypes.createconst createStoreUncurried = <T>( stateCreator: ZustandExportedTypes.StateCreator<T>,) => { const store = actualCreateStore(stateCreator) const initialState = store.getInitialState() storeResetFns.add(() => { store.setState(initialState, true) }) return store}// when creating a store, we get its initial state, create a reset function and add it in the setexport const createStore = (<T>( stateCreator: ZustandExportedTypes.StateCreator<T>,) => { console.log('zustand createStore mock') // to support curried version of createStore return typeof stateCreator === 'function' ? createStoreUncurried(stateCreator) : createStoreUncurried}) as typeof ZustandExportedTypes.createStore// reset all stores after each test runafterEach(() => { act(() => { storeResetFns.forEach((resetFn) => { resetFn() }) })})
2
Setup Test Environment
setup-jest.ts
import '@testing-library/jest-dom'
3
Configure Jest
jest.config.ts
import type { JestConfigWithTsJest } from 'ts-jest'const config: JestConfigWithTsJest = { preset: 'ts-jest', testEnvironment: 'jsdom', setupFilesAfterEnv: ['./setup-jest.ts'],}export default config
To use TypeScript, install ts-jest and ts-node.
In Vitest, you can change the root. Make sure to create your __mocks__ directory in the correct location. If you set root to ./src, create ./src/__mocks__ instead of ./__mocks__.
1
Create Zustand Mock
__mocks__/zustand.ts
import { act } from '@testing-library/react'import type * as ZustandExportedTypes from 'zustand'export * from 'zustand'const { create: actualCreate, createStore: actualCreateStore } = await vi.importActual<typeof ZustandExportedTypes>('zustand')// a variable to hold reset functions for all stores declared in the appexport const storeResetFns = new Set<() => void>()const createUncurried = <T>( stateCreator: ZustandExportedTypes.StateCreator<T>,) => { const store = actualCreate(stateCreator) const initialState = store.getInitialState() storeResetFns.add(() => { store.setState(initialState, true) }) return store}export const create = (<T>( stateCreator: ZustandExportedTypes.StateCreator<T>,) => { console.log('zustand create mock') return typeof stateCreator === 'function' ? createUncurried(stateCreator) : createUncurried}) as typeof ZustandExportedTypes.createconst createStoreUncurried = <T>( stateCreator: ZustandExportedTypes.StateCreator<T>,) => { const store = actualCreateStore(stateCreator) const initialState = store.getInitialState() storeResetFns.add(() => { store.setState(initialState, true) }) return store}export const createStore = (<T>( stateCreator: ZustandExportedTypes.StateCreator<T>,) => { console.log('zustand createStore mock') return typeof stateCreator === 'function' ? createStoreUncurried(stateCreator) : createStoreUncurried}) as typeof ZustandExportedTypes.createStore// reset all stores after each test runafterEach(() => { act(() => { storeResetFns.forEach((resetFn) => { resetFn() }) })})
Without globals configuration, add import { afterEach, vi } from 'vitest' at the top.