Skip to main content

Testing SoulState Stores

SoulState is designed with systems-grade testability in mind. Because updates are deterministic and propagation is topological, you can test complex reactive behaviors with high confidence.

Unit Testing Logic

Testing SoulState stores is straightforward because they are plain JavaScript objects.

import { createStore } from 'soulstate';

test('should increment count', () => {
const store = createStore({ count: 0 });
store.setState(s => ({ count: s.count + 1 }));

expect(store.getState().count).toBe(1);
});

Testing Async Propagation

Since SoulState defaults to microtask batching, you must wait for the microtask queue to flush before asserting on listeners or computeds.

test('should update computed value after microtask', async () => {
const store = createStore({ a: 1, b: 2 });
const sum = store.computed(s => s.a + s.b);

store.setState({ a: 10 });

// 1. Invalidation is scheduled but not yet run
expect(sum.value).toBe(3); // Returns cached old value

// 2. Wait for the microtask to flush
await Promise.resolve();

// 3. Now re-computed
expect(sum.value).toBe(12);
});

Testing Glitch-Free Behavior

You can verify that SoulState's Topological Engine prevents intermediate states in diamond dependency chains.

test('should prevent glitches in diamond dependency', async () => {
const store = createStore({ a: 1 });
const b = store.computed(s => s.a + 1);
const c = store.computed(s => s.a + 1);

const dRuns = vi.fn();
const d = store.computed(() => {
dRuns();
return b.value + c.value;
});

// Initial run
expect(d.value).toBe(4);
expect(dRuns).toHaveBeenCalledTimes(1);

store.setState({ a: 2 });
await Promise.resolve();

// 'd' should only re-run ONCE, after both 'b' and 'c' have settled
expect(d.value).toBe(6);
expect(dRuns).toHaveBeenCalledTimes(2);
});

Stress Testing & Scalability

For systems-grade applications, we recommend verifying that your propagation durations remain within acceptable limits at scale.

test('should handle 10,000 subscribers efficiently', async () => {
const store = createStore({ count: 0 });
let callCount = 0;

for (let i = 0; i < 10000; i++) {
store.subscribe(s => s.count, () => callCount++);
}

const start = performance.now();
store.setState({ count: 1 });
await Promise.resolve();
const duration = performance.now() - start;

console.log(`Propagation took ${duration}ms`);
expect(callCount).toBe(10000);
expect(duration).toBeLessThan(50); // Should be very fast
});

Deterministic CI

Because SoulState does not rely on random timers or non-deterministic scheduling, your tests will be stable and repeatable across different CI environments.