A lightweight TypeScript test runner built from scratch to understand the core principles of Jest/Vitest.
Assertify is a minimal test framework that demonstrates the fundamental concepts of test runners:
- Test Discovery: Automatically finds and runs
*.test.tsfiles using glob patterns - Sandboxed Execution: Uses Node.js
vmmodule to isolate test execution contexts - Async Support: Full support for
async/awaitin test cases - Concise API: Core implementation under 500 lines of code
describe(name, fn)- Group related testsit(name, fn)- Define a test caseexpect(actual)- Make assertionsbeforeEach(fn)- Setup before each testafterEach(fn)- Teardown after each test
toBe(expected)- Strict equality (===)toEqual(expected)- Deep equality for objects and arraystoBeTruthy()- Check if value is truthytoBeFalsy()- Check if value is falsytoBeDefined()- Check if value is not undefinedtoBeNull()- Check if value is nulltoThrow(error?)- Check if function throwstoHaveLength(expected)- Check array/string lengthtoContain(expected)- Check if array/string contains valuetoBeCloseTo(expected, precision?)- Check floating point equality
npm install
npm run buildCreate test files with the pattern *.test.ts:
import { describe, it, expect } from "assertify";
describe("Math operations", () => {
it("should add two numbers", () => {
expect(2 + 2).toBe(4);
});
it("should handle async operations", async () => {
const result = await Promise.resolve(42);
expect(result).toBe(42);
});
});Run all tests:
npm run test
# or
node bin/assertify.jsRun a specific test file:
node bin/assertify.js examples/math.test.jsNote: The test runner executes JavaScript files. If writing tests in TypeScript, compile them first:
tsc --outDir test --target ES2022 --module ES2022 test/**/*.test.tsassertify/
├── src/
│ ├── index.ts # Main entry (exports describe, it, expect)
│ ├── runner.ts # Test discovery and execution (uses glob and vm)
│ ├── testContext.ts # Test context management
│ ├── expect.ts # Expect implementation with Proxy
│ ├── matchers.ts # Assertion matchers (toBe, toEqual, etc.)
│ ├── equals.ts # Deep equality comparison
│ └── ExpectationError.ts # Custom error class
├── bin/
│ └── assertify.js # CLI entry point
├── examples/ # Example test files
│ ├── math.test.ts
│ └── async.test.ts
├── package.json
├── tsconfig.json
└── README.md
Uses the glob package to find all *.test.ts files recursively, ignoring node_modules and dist directories.
Leverages Node.js vm module to create isolated execution contexts for each test file, preventing test contamination:
const sandbox = createContext({
console,
setTimeout,
// ... other globals
require: (module) => {
if (module === "assertify") return api;
// ...
}
});Test functions can return Promises, which are automatically awaited:
it("async test", async () => {
const result = await someAsyncOperation();
expect(result).toBe(expected);
});- 5 Core APIs:
describe,it,expect,beforeEach,afterEach - 10+ Matchers:
toBe,toEqual,toBeTruthy,toBeFalsy,toBeDefined,toBeNull,toThrow,toHaveLength,toContain,toBeCloseTo - Core Implementation: ~400 lines of code
- Key Technologies: Node.js
vmmodule,globfor file discovery, TypeScript
See the examples/ directory for complete examples:
math.test.ts- Basic math operations and assertionsasync.test.ts- Async/await tests with setup/teardown
Build TypeScript:
npm run buildRun tests:
npm run testMIT