Fresh Ward Per Test
Problem
Tests that share a single createWard() call can interfere with each other if one test's expectations depend on the initial rule set while another modifies the instance. You want each test to start from a clean, predictable state.
Solution
Because createWard() is synchronous and cheap, call it inside a beforeEach block to create a fresh instance per test. There is no snapshot-and-restore mechanism — immutability means a new instance is the clean state.
ts
import { beforeEach, describe, expect, it } from 'vitest';
import { createWard } from '@vielzeug/ward';
function createTestWard() {
return createWard([{ role: 'viewer', resource: 'posts', action: 'read', effect: 'allow' }]);
}
describe('post permissions', () => {
let ward = createTestWard();
beforeEach(() => {
// Each test gets an independent instance
ward = createTestWard();
});
it('allows viewers to read posts', () => {
expect(ward.can({ id: 'u1', roles: ['viewer'] }, 'posts', 'read')).toBe(true);
});
it('denies viewers to delete posts', () => {
expect(ward.can({ id: 'u1', roles: ['viewer'] }, 'posts', 'delete')).toBe(false);
});
});Pitfalls
- Sharing one ward instance across test files via a module-level
constis fine as long as all tests treat it as read-only. Defining rules at module scope and checking decisions in tests is the most common correct pattern. - Avoid constructing the ward inside individual
itblocks when multiple tests share the same rule set — preferbeforeEachto keep test bodies focused on assertions.