Node.js 18's node:test, as an npm package providing comprehensive testing framework for Node.js 14+
—
Hook functions that run at specific points in the test lifecycle for setup and teardown operations. These hooks provide a clean way to prepare test environments and clean up resources.
Runs once before all tests in the current suite. Ideal for expensive setup operations that can be shared across multiple tests.
/**
* Runs once before all tests in the current suite
* @param fn - Setup function to execute
* @param options - Optional hook configuration
*/
function before(fn: () => void | Promise<void>, options?: { signal?: AbortSignal, timeout?: number }): void;Usage Examples:
import { describe, it, before } from "test";
describe("Database tests", () => {
before(async () => {
// Setup database connection
await connectToDatabase();
await runMigrations();
});
it("should create user", () => {
// Test implementation
});
it("should update user", () => {
// Test implementation
});
});
// Top-level before hook
before(() => {
console.log("Starting all tests");
process.env.NODE_ENV = "test";
});Runs once after all tests in the current suite have completed. Used for cleanup operations that should happen regardless of test success or failure.
/**
* Runs once after all tests in the current suite
* @param fn - Cleanup function to execute
* @param options - Optional hook configuration
*/
function after(fn: () => void | Promise<void>, options?: { signal?: AbortSignal, timeout?: number }): void;Usage Examples:
import { describe, it, before, after } from "test";
describe("File system tests", () => {
before(() => {
// Create test directory
fs.mkdirSync('./test-files');
});
after(() => {
// Clean up test directory
fs.rmSync('./test-files', { recursive: true });
});
it("should create file", () => {
// Test implementation
});
});
// Top-level after hook
after(async () => {
console.log("All tests completed");
await cleanup();
});Runs before each individual test. Perfect for ensuring each test starts with a clean, predictable state.
/**
* Runs before each individual test
* @param fn - Setup function to execute before each test
* @param options - Optional hook configuration
*/
function beforeEach(fn: () => void | Promise<void>, options?: { signal?: AbortSignal, timeout?: number }): void;Usage Examples:
import { describe, it, beforeEach } from "test";
describe("Calculator tests", () => {
let calculator;
beforeEach(() => {
// Fresh calculator instance for each test
calculator = new Calculator();
calculator.reset();
});
it("should add numbers", () => {
const result = calculator.add(2, 3);
if (result !== 5) throw new Error("Addition failed");
});
it("should subtract numbers", () => {
const result = calculator.subtract(5, 3);
if (result !== 2) throw new Error("Subtraction failed");
});
});
// Async beforeEach
describe("API tests", () => {
beforeEach(async () => {
await resetDatabase();
await seedTestData();
});
it("should fetch users", async () => {
// Test implementation
});
});Runs after each individual test completes. Used for cleaning up test-specific resources and ensuring tests don't interfere with each other.
/**
* Runs after each individual test
* @param fn - Cleanup function to execute after each test
* @param options - Optional hook configuration
*/
function afterEach(fn: () => void | Promise<void>, options?: { signal?: AbortSignal, timeout?: number }): void;Usage Examples:
import { describe, it, beforeEach, afterEach } from "test";
describe("Cache tests", () => {
beforeEach(() => {
// Initialize cache
cache.init();
});
afterEach(() => {
// Clear cache after each test
cache.clear();
});
it("should store values", () => {
cache.set("key", "value");
if (cache.get("key") !== "value") {
throw new Error("Cache store failed");
}
});
it("should handle expiration", async () => {
cache.set("key", "value", { ttl: 10 });
await new Promise(resolve => setTimeout(resolve, 20));
if (cache.get("key") !== null) {
throw new Error("Cache expiration failed");
}
});
});
// Async afterEach
describe("Resource tests", () => {
afterEach(async () => {
await closeConnections();
await cleanupTempFiles();
});
it("should manage resources", () => {
// Test implementation
});
});Hooks execute in the following order for nested suites:
before hooksbefore hooksbeforeEach hooksbeforeEach hooksafterEach hooksafterEach hooksafter hooksafter hooksExample:
import { describe, it, before, after, beforeEach, afterEach } from "test";
before(() => console.log("1. Global before"));
after(() => console.log("8. Global after"));
describe("Outer suite", () => {
before(() => console.log("2. Outer before"));
beforeEach(() => console.log("3. Outer beforeEach"));
afterEach(() => console.log("6. Outer afterEach"));
after(() => console.log("7. Outer after"));
describe("Inner suite", () => {
before(() => console.log("3. Inner before"));
beforeEach(() => console.log("4. Inner beforeEach"));
afterEach(() => console.log("5. Inner afterEach"));
after(() => console.log("6. Inner after"));
it("test case", () => {
console.log("5. Test execution");
});
});
});If a hook throws an error or returns a rejected promise:
describe("Error handling", () => {
before(() => {
throw new Error("Setup failed");
// This will cause all tests in this suite to be skipped
});
afterEach(() => {
// This runs even if the test or beforeEach failed
cleanup();
});
it("this test will be skipped", () => {
// Won't run due to before hook failure
});
});Hooks only apply to tests within their scope:
// Global hooks - apply to all tests
before(() => {
// Runs before any test
});
describe("Suite A", () => {
// Suite-level hooks - only apply to tests in this suite
beforeEach(() => {
// Only runs before tests in Suite A
});
it("test 1", () => {});
it("test 2", () => {});
});
describe("Suite B", () => {
// Different suite-level hooks
beforeEach(() => {
// Only runs before tests in Suite B
});
it("test 3", () => {});
});describe("Best practices example", () => {
let server;
let client;
before(async () => {
// Expensive setup once per suite
server = await startTestServer();
});
beforeEach(() => {
// Fresh client for each test
client = new ApiClient(server.url);
});
afterEach(() => {
// Clean up test-specific resources
client.close();
});
after(async () => {
// Clean up suite-level resources
await server.close();
});
it("should connect", async () => {
await client.connect();
if (!client.isConnected()) {
throw new Error("Connection failed");
}
});
});Install with Tessl CLI
npx tessl i tessl/npm-test