Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon What are the differences in testing Zustand with Jest vs Vitest


What are the differences in testing Zustand with Jest vs Vitest


Testing Zustand stores with Jest and Vitest involves similar steps, but there are some differences in the setup and configuration. Here are the key differences:

Jest Setup

1. Mocking Zustand: Jest provides built-in support for mocking, which allows you to simulate the behavior of Zustand stores during testing. You can create a mock for Zustand by using the `jest.requireActual` function to get the actual implementation of Zustand and then create a mock that wraps around it.

2. Resetting Stores: To ensure that each test runs with a clean slate, you need to reset the Zustand stores after each test. This can be done using Jest's `afterEach` hook.

3. Shared Code: To avoid code duplication, you can create a shared module that contains the Zustand store creator and use it in both Jest and Vitest setups.

Vitest Setup

1. Mocking Zustand: Vitest also supports mocking Zustand, but it requires a slightly different approach. You need to create a mock for Zustand in the `__mocks__` directory and then import it in your tests.

2. Resetting Stores: In Vitest, you need to use the `afterEach` hook to reset the Zustand stores after each test.

3. Shared Code: Similar to Jest, you can create a shared module for Zustand store creators to avoid code duplication.

Key Differences

1. Module Management: Jest is designed to work with CommonJS modules, while Vitest is designed to work with ECMAScript Modules (ESM). This means that if your project uses ESM, you might need to adjust your setup accordingly.

2. Community Support: Jest has a larger community and more extensive documentation, which can be beneficial for developers who are new to testing.

3. Test Speed: Vitest is known for its fast test execution, which can be beneficial for projects that require extensive testing.

Example Setup

Here is an example of how you might set up Jest and Vitest for testing Zustand stores:

Jest Setup

javascript
// __mocks__/zustand.ts
import * as zustand from 'zustand';
import { act } from '@testing-library/react';

const { create: actualCreate, createStore: actualCreateStore } = jest.requireActual<typeof zustand>('zustand');

const storeResetFns = new Set<() => void>();

const createUncurried = <T>(stateCreator: zustand.StateCreator<T>) => {
  const store = actualCreate(stateCreator);
  const initialState = store.getInitialState();
  storeResetFns.add(() => {
    store.setState(initialState, true);
  });
  return store;
};

export const create = <T>(stateCreator: zustand.StateCreator<T>) => {
  console.log('zustand create mock');
  return typeof stateCreator === 'function'
    ? createUncurried(stateCreator)
    : createUncurried;
};

const createStoreUncurried = <T>(stateCreator: zustand.StateCreator<T>) => {
  const store = actualCreateStore(stateCreator);
  const initialState = store.getInitialState();
  storeResetFns.add(() => {
    store.setState(initialState, true);
  });
  return store;
};

export const createStore = <T>(stateCreator: zustand.StateCreator<T>) => {
  console.log('zustand createStore mock');
  return typeof stateCreator === 'function'
    ? createStoreUncurried(stateCreator)
    : createStoreUncurried;
};

// setup-jest.ts
import '@testing-library/jest-dom';
vi.mock('zustand');

// global.d.ts
/// <reference types="zustand" />
/// <reference types="vitest/globals" />

// tests
import { create } from './__mocks__/zustand';
import { createStore } from './__mocks__/zustand';

describe('Zustand Store', () => {
  let store: any;

  beforeEach(() => {
    store = createStore(counterStoreCreator);
  });

  afterEach(() => {
    act(() => {
      storeResetFns.forEach((resetFn) => {
        resetFn();
      });
    });
  });

  it('should increment the count', () => {
    // test logic
  });
});

Vitest Setup

javascript
// __mocks__/zustand.ts
import * as zustand from 'zustand';
import { act } from '@testing-library/react';

const { create: actualCreate, createStore: actualCreateStore } = jest.requireActual<typeof zustand>('zustand');

const storeResetFns = new Set<() => void>();

const createUncurried = <T>(stateCreator: zustand.StateCreator<T>) => {
  const store = actualCreate(stateCreator);
  const initialState = store.getInitialState();
  storeResetFns.add(() => {
    store.setState(initialState, true);
  });
  return store;
};

export const create = <T>(stateCreator: zustand.StateCreator<T>) => {
  console.log('zustand create mock');
  return typeof stateCreator === 'function'
    ? createUncurried(stateCreator)
    : createUncurried;
};

const createStoreUncurried = <T>(stateCreator: zustand.StateCreator<T>) => {
  const store = actualCreateStore(stateCreator);
  const initialState = store.getInitialState();
  storeResetFns.add(() => {
    store.setState(initialState, true);
  });
  return store;
};

export const createStore = <T>(stateCreator: zustand.StateCreator<T>) => {
  console.log('zustand createStore mock');
  return typeof stateCreator === 'function'
    ? createStoreUncurried(stateCreator)
    : createStoreUncurried;
};

// setup-vitest.ts
import '@testing-library/jest-dom';
vi.mock('zustand');

// vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import viteConfig from './vite.config';

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

// tests
import { create } from './__mocks__/zustand';
import { createStore } from './__mocks__/zustand';

describe('Zustand Store', () => {
  let store: any;

  beforeEach(() => {
    store = createStore(counterStoreCreator);
  });

  afterEach(() => {
    act(() => {
      storeResetFns.forEach((resetFn) => {
        resetFn();
      });
    });
  });

  it('should increment the count', () => {
    // test logic
  });
});

In summary, while Jest and Vitest have different approaches to mocking and resetting Zustand stores, the core principles of setting up Zustand for testing remain the same. By following the guidelines provided in the Zustand documentation and adjusting the setup according to your testing framework, you can effectively test your Zustand stores.

Citations:
[1] https://docs.pmnd.rs/zustand/guides/testing
[2] https://github.com/pmndrs/zustand/blob/main/docs/guides/testing.md
[3] https://saucelabs.com/resources/blog/vitest-vs-jest-comparison
[4] https://github.com/pmndrs/zustand/discussions/1918
[5] https://www.reddit.com/r/typescript/comments/1918d0d/testing_react_apps/