# Test Zustand stores

Use a DOM test environment for React components and use React Testing Library to exercise the rendered component. Jest and Vitest differ in their module-loading and test-runner configuration.

## When to use this pattern

Use this setup when tests create stores with [`create`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#create) or [`createStore`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#createstore). Component tests interact with the rendered UI rather than calling implementation details.

## Share the store creator

For the typed creator pattern, see [Typed store patterns](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/typed-store-patterns); this page applies that shared creator to test setup and assertions.

## Configure Jest

Install the test dependencies, including `ts-jest` and `ts-node` for TypeScript configuration:

```bash
npm install -D jest ts-jest ts-node jest-environment-jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event
```

Add the DOM matchers and configure Jest to use JSDOM:

```ts title="setup-jest.ts"
import '@testing-library/jest-dom'
```

```ts title="jest.config.ts"
const config = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['./setup-jest.ts'],
}

export default config
```

The result is a Jest environment with a JSDOM document and Testing Library matchers.

## Configure Vitest

Install the Vitest and DOM-testing dependencies:

```bash
npm install -D vitest jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event
```

Enable JSDOM and the Testing Library matchers in the setup file:

```text title="setup-vitest.ts"
import '@testing-library/jest-dom/vitest'

```

```ts title="vitest.config.ts"
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: ['./setup-vitest.ts'],
  },
})
```

With globals disabled, import the Vitest globals used by your test files from `vitest`, and omit the `vitest/globals` type reference from `global.d.ts`. With globals enabled, add the Vitest type reference:

```ts title="global.d.ts"
/// <reference types="vite/client" />
/// <reference types="vitest/globals" />
```

## Test a React component

The component uses the hook returned by `create` and selects state and the action separately. [`useStore`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#usestore) is needed when a component consumes a standalone store; this component does not need it.

```tsx title="Counter.tsx"
import { create } from 'zustand'
import { counterStoreCreator } from './shared/counter-store-creator'

export const useCounterStore = create<{
  count: number
  inc: () => void
}>()(counterStoreCreator)

export function Counter() {
  const count = useCounterStore((state) => state.count)
  const inc = useCounterStore((state) => state.inc)

  return (
    <div>
      <output aria-label="count">{count}</output>
      <button type="button" onClick={inc}>Increment</button>
    </div>
  )
}
```

Render the component, assert the initial UI, click the user-facing button, and assert the updated UI:

```tsx title="Counter.test.tsx"
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Counter } from './Counter'

test('increments the displayed count', async () => {
  const user = userEvent.setup()
  render(<Counter />)

  expect(screen.getByLabelText('count')).toHaveTextContent('1')
  await user.click(screen.getByRole('button', { name: 'Increment' }))

  expect(screen.getByLabelText('count')).toHaveTextContent('2')
})
```

The test sees `1` on the initial render and `2` after the click.

## Test a standalone store

For the `StoreApi` methods, see [How Zustand works](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/how-zustand-works); this test uses `getState()` to invoke the action and inspect the resulting state.

```ts title="counter-store.ts"
import { createStore } from 'zustand/vanilla'
import { counterStoreCreator } from './shared/counter-store-creator'

export const counterStore = createStore<{
  count: number
  inc: () => void
}>()(counterStoreCreator)
```

```ts title="counter-store.test.ts"
import { counterStore } from './counter-store'

test('updates a standalone store through its action', () => {
  expect(counterStore.getState().count).toBe(1)

  counterStore.getState().inc()

  expect(counterStore.getState().count).toBe(2)
})
```

The test reads the state before and after the action.

## Use a vanilla store in a component

For a component that receives or imports a standalone store, bind it with `useStore` and select only the value it renders.

```tsx title="VanillaCounter.tsx"
import { useStore } from 'zustand'
import { counterStore } from './counter-store'

export function VanillaCounter() {
  const count = useStore(counterStore, (state) => state.count)
  const inc = useStore(counterStore, (state) => state.inc)

  return (
    <div>
      <output aria-label="count">{count}</output>
      <button type="button" onClick={inc}>Increment</button>
    </div>
  )
}
```

The hook returns the selected value and subscribes the component to changes. Test it with the same rendered interaction: the component displays `1`, and the button changes it to `2`.

## Pitfalls

- Use JSDOM for component tests. A test runner without a DOM cannot render React components.
- For selector identity and shallow-rendering pitfalls, see [Selectors and rendering](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/selectors-and-rendering). For server-rendered stores, see [Handle server-rendered stores](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/handle-server-rendered-stores).

## Related

- [Build a React store](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/build-a-react-store)
- [Create a vanilla store](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/create-a-vanilla-store)
- [Reset store state](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/reset-store-state)
- [React quick start](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/react-quick-start)
