Lightweight and fully type-safe lazy test variables for Bun, Vitest, Jest, and Node.js.
Inspired by RSpec.
When writing tests with complex setups, we frequently declare let variables and re-assign them in beforeEach. This is repetitive and brittle.
// β The standard (but annoying) way
import { describe, beforeEach, it } from "vitest";
describe("User", () => {
let firstName: string;
let fullName: string;
beforeEach(() => {
firstName = "John";
fullName = `${firstName} Doe`;
});
describe("when first name changes", () => {
beforeEach(() => {
firstName = "Jane";
fullName = `${firstName} Doe`; // Manual re-evaluation!
});
it("is updated", () => { /* ... */ });
});
});testoise is a modern, fully type-safe, and lightweight alternative to bdd-lazy-var. It brings the power and elegance of RSpec's let directly into JavaScript!
- Evaluates Lazily: Factory is only executed upon
get(). - Caches Per Test: Return value is cached for the duration of a single
itblock. - Overrides Cleanly: Nested blocks override parents; dependents react automatically.
bun add -d @ilamy/testoise # or npm install --save-dev @ilamy/testoiseImport def, get, and optionally testoise from your test runner's specific adapter path:
- Bun:
import { def, get, testoise } from "@ilamy/testoise/bun" - Vitest:
import { def, get, testoise } from "@ilamy/testoise/vitest" - Jest:
import { def, get, testoise } from "@ilamy/testoise/jest" - Node.js:
import { def, get, testoise } from "@ilamy/testoise/node"
import { describe, it } from "node:test";
import { def, get } from "@ilamy/testoise/node";
describe("User", () => {
def("firstName", () => "John");
def("lastName", () => "Doe");
// 2. Variables can depend on other lazy variables!
def("fullName", () => `${get("firstName")} ${get("lastName")}`);
it("uses the default name", () => {
// Manual type casting for those not using the suite wrapper
expect(get<string>("fullName")).toBe("John Doe");
});
describe("when first name changes", () => {
// 3. Overrides the "firstName" variable for this describe block only
def("firstName", () => "Jane");
it("automatically updates dependent variables", () => {
expect(get<string>("fullName")).toBe("Jane Doe");
});
});
});For strong type inference without manual casting, testoise provides a Suite Wrapper. This is the recommended way to use testoise in TypeScript projects.
import { expect, it } from "vitest";
import { testoise } from "@ilamy/testoise/vitest";
// 1. Define your registry
interface MyVars {
user: { name: string; age: number };
isAdmin: boolean;
}
// 2. Use the wrapper for automatic inference π’
testoise<MyVars>("User Suite", ({ def, get }) => {
def("user", () => ({ name: "Alice", age: 30 }));
def("isAdmin", () => get("user").age > 21);
it("knows Alice is an adult", () => {
const user = get("user"); // Automatically inferred!
expect(get("isAdmin")).toBe(true);
});
// Now you can use standard describe or simplified testoise!
describe("when user is underage", () => {
def("user", () => ({ name: "Bob", age: 15 }));
it("knows Bob is not an admin", () => {
expect(get("isAdmin")).toBe(false);
});
});
});
Tip
The suite wrapper ensures that your variable names and types are always in sync, preventing runtime errors and providing a premium developer experience.
Explore our examples/ directory for practical, framework-specific setups:
- Bun Examples: Native Bun testing with DOM support.
- Vitest Examples: Vitest integration with
happy-dom. - Jest Examples: Jest integration with
jsdomand SWC. - Node.js Examples: Built-in Node.js test runner with
happy-dom.
| Feature | Vitest | Bun | Jest | Node.js |
|---|---|---|---|---|
| Lazy Evaluation | β | β | β | β |
| Context Nesting | β | β | β | β |
| Automatic Inference | β | β | β | β |
| Redefinition Protection | β | β | β | β |
| React Support | β | β | β | β |
| Vue Support | β | β | β | β |
Registers a variable.
Evaluates and returns the factory result. Caches per test. Supports manual type casting: get<string>("name").
Suite wrapper for simplified type inference across a suite. Provides a typed api with its own def and get methods. Use standard describe blocks for nesting; type safety will be maintained throughout the scope.
MIT Β© Sujeet KC