[Proposal] š Testing utilities for zustand
See original GitHub issueBackground
zustand
is an amazing package that doesnāt depend on React Context providers at all. The stores live āoutsideā of React and can be accessed from everywhere, even outside of React components.
Because of this, when testing React components via a testing library, the states of the stores donāt reset across tests. That means that the code of one test may affect the outcome of another test. It also means that within a test suite (i.e. describe
block), test order matters, since a test might modify the state of a store that another test may be depending upon.
Thereās currently no official way of resetting store state before/after each test. A custom solution would include manually extracting the initialState
of a store and using it to force-set a storeās state before each test run:
const initialStoreState = useStore.getState();
describe('Suite', () => {
beforeEach(() => {
useStore.setState(initialStoreState, true);
});
... // tests
});
This can quickly become tedious since:
- You have to manually do that for each test suite/file
- You have to do that for each and every store in your app or remember the exact stores your tests will be using
An alternative solution involves āmockingā zustand and automatically resetting all stores after each test by keeping track of all stores in the app. This involves creating a __mocks__/zustand.js
file and putting the following code in there:
import actualCreate from 'zustand';
// a variable to hold reset functions for all stores declared in the app
const storeResetFns = new Set();
// when creating a store, we get its initial state, create a reset function and add it in the set
const create = createState => {
const store = actualCreate(createState);
const initialState = store.getState();
storeResetFns.add(() => store.setState(initialState, true));
return store;
};
// Reset all stores after each test run
afterEach(() => {
storeResetFns.forEach(resetFn => resetFn());
});
This has the benefits of:
- Not having to modify your source code or tests in any shape or form
- Not having to do anything for each new store you create
Goal
Have an officially supported way of testing zustand, while limiting the developerās effort to achieve that
Proposal
We create a separate package named zustand/testing
which will contain a polished & safe version of the snippet above. From there there are 2 options:
Option 1 - Automatic mocking
This option automatically mocks zustand
ās default export, without the developer needing to create any mock files.
Whenever process.env.NODE_ENV
has a value of test
(which currently stands true for all major test runners), zustand
will silently replace the default create
function with a āmockedā version of create
(available via zustand/testing
) that will enable the reset functionality portrayed above.
In order to be backwards compatible & avoid any breaking changes, this can be controlled by an additional additional ENV variable named ZUSTAND_SKIP_AUTO_RESET
which will default to true
in the current major version and to false
in the next one.
Option 2 - Manual mocking
This option requires developers to explicitly opt-in to the mocking behavior. Specifically, developers will have to import zustand/testing
within their setup file in order to have the mocking capability enabled.
This removes the need for process.env.NODE_ENV
and process.env.ZUSTAND_SKIP_AUTO_RESET
, but requires an explicit opt-in.
Related Links
Issue Analytics
- State:
- Created 3 years ago
- Reactions:23
- Comments:14 (10 by maintainers)
Top GitHub Comments
jest.isolateModules() might also be an option to reset the state.
Just starting out with Zustand, so I cannot comment yet on how tedious it becomes - we wrote this little helper here:
which you can the use like that:
this can be easily extended to accept a number of such hooks, soā¦I guess usage will pick up gradually now, we shall see how tedious this becomes