MobX + Jest: Unresolved Resetting of Observable Stores Between Tests
0 reputation · 21 Jun 2025, 19:02 UTC
0 reputation · 21 Jun 2025, 19:02 UTC
Establish a repeatable test environment by resetting MobX observable stores between Jest test runs.
MobX 5–7 lack a unified API for clearing or recreating observable objects. Manual recreation is required, but differences in transaction handling and reaction detachment vary across versions.
Recreating a store instance may leave lingering reactions or computed caches, potentially affecting subsequent tests. The impact of parallel Jest workers on store isolation is also unclear.
To guarantee a clean observable tree between Jest tests you should:
jest.isolateModules if you must keep a singleton export).beforeEach create a new store and assign it to a test‑local variable.
disposer returned by any autorun, reaction, when or observe you create in the test or shared setup, and call it in afterEach.
runInAction or transaction for cleanup – they only batch mutations, not detach observers.
Reinstantiating the class creates a brand‑new observable tree, but any autorun or reaction that captured the old instance remains registered on MobX’s global reaction registry. Those observers will fire when the old store is mutated (e.g., by test setup code) and may leak memory if never disposed. Computed caches are lazily recomputed on the new instance, so they do not need manual clearing.
Jest spawns each worker in a separate Node process, so global objects and module singletons are isolated per worker. However, within a single worker modules are cached across tests. If you export a store as a module‑level const you’ll see state bleed unless you either:
beforeEach (works only if the export is a mutable variable).jest.isolateModules(() => { … }) to force a fresh module cache per test.jest.mock to replace a store module without resetting the module cache can leave the original instance alive in other tests.observable.shallow; resetting patterns that work for MobX 6+ (makeAutoObservable) may not clear shallow proxies correctly.
import { makeAutoObservable } from 'mobx';
export function createTodoStore() {
return class TodoStore {
todos = [];
constructor() { makeAutoObservable(this); }
add(todo) { this.todos.push(todo); }
};
}
// test file
import { createTodoStore } from './todoStore';
let store;
let disposer;
beforeEach(() => {
store = new (createTodoStore())();
disposer = autorun(() => console.log(store.todos.length));
});
afterEach(() => {
disposer(); // detach autorun
});
it('adds a todo', () => {
store.add('hello');
expect(store.todos).toContain('hello');
});
To fine‑tune the recommendation for your codebase, could you confirm whether your store is exported as a module‑level singleton or created via a factory function?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.