Usage

The 14 examples below are taken from zig-test-framework's README.

.{
    .name = "my-project",
    .version = "0.1.0",
    .dependencies = .{
        .@"zig-test-framework" = .{
            .url = "https://github.com/zig-utils/zig-test-framework/archive/refs/tags/v0.1.0.tar.gz",
            // Replace with actual hash after publishing
        },
    },
}
// tests/math.test.zig
const std = @import("std");

test "addition works" {
    const result = 2 + 2;
    try std.testing.expectEqual(@as(i32, 4), result);
}

test "subtraction works" {
    const result = 10 - 5;
    try std.testing.expectEqual(@as(i32, 5), result);
}
const std = @import("std");
const ztf = @import("zig-test-framework");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer * = gpa.deinit();
    const allocator = gpa.allocator();

    // Define a test suite
    try ztf.describe(allocator, "Math operations", struct {
        fn testSuite(alloc: std.mem.Allocator) !void {
            try ztf.it(alloc, "should add two numbers", testAddition);
            try ztf.it(alloc, "should subtract two numbers", testSubtraction);
        }

        fn testAddition(alloc: std.mem.Allocator) !void {
            const result = 2 + 2;
            try ztf.expect(alloc, result).toBe(4);
        }

        fn testSubtraction(alloc: std.mem.Allocator) !void {
            const result = 10 - 5;
            try ztf.expect(alloc, result).toBe(5);
        }
    }.testSuite);

    // Run all tests
    const registry = ztf.getRegistry(allocator);
    const success = try ztf.runTests(allocator, registry);

    // Clean up the registry
    ztf.cleanupRegistry();

    if (!success) {
        std.process.exit(1);
    }
}
// Basic equality
try expect(alloc, 5).toBe(5);
try expect(alloc, true).toBeTruthy();
try expect(alloc, false).toBeFalsy();

// Negation
try expect(alloc, 5).not().toBe(10);

// Comparisons
try expect(alloc, 10).toBeGreaterThan(5);
try expect(alloc, 10).toBeGreaterThanOrEqual(10);
try expect(alloc, 5).toBeLessThan(10);
try expect(alloc, 5).toBeLessThanOrEqual(5);

// Strings
try expect(alloc, "hello").toBe("hello");
try expect(alloc, "hello world").toContain("world");
try expect(alloc, "hello").toStartWith("hel");
try expect(alloc, "hello").toEndWith("lo");
try expect(alloc, "hello").toHaveLength(5);

// Optionals
const value: ?i32 = null;
try expect(alloc, value).toBeNull();

const defined: ?i32 = 42;
try expect(alloc, defined).toBeDefined();

// Arrays/Slices
const numbers = [*]i32{1, 2, 3, 4, 5};
const matcher = expectArray(alloc, &numbers);
try matcher.toHaveLength(5);
try matcher.toContain(3);
try matcher.toContainAll(&[*]i32{1, 3, 5});

// Error assertions
const FailingFn = struct {
    fn call() !void {
        return error.TestError;
    }
};
try expect(alloc, FailingFn.call).toThrow();
try expect(alloc, FailingFn.call).toThrowError(error.TestError);
// Floating-point comparison
try ztf.toBeCloseTo(0.1 + 0.2, 0.3, 10);

// NaN and Infinity
try ztf.toBeNaN(std.math.nan(f64));
try ztf.toBeInfinite(std.math.inf(f64));

// Struct matching
const User = struct {
    name: []const u8,
    age: u32,
};

const user = User{ .name = "Alice", .age = 30 };
const matcher = ztf.expectStruct(alloc, user);
try matcher.toHaveField("name", "Alice");
try matcher.toHaveField("age", @as(u32, 30));
try ztf.describe(allocator, "Database tests", struct {
    var db*connection: ?*Database = null;

    fn testSuite(alloc: std.mem.Allocator) !void {
        // Runs once before all tests
        try ztf.beforeAll(alloc, setupDatabase);

        // Runs before each test
        try ztf.beforeEach(alloc, openConnection);

        // Runs after each test
        try ztf.afterEach(alloc, closeConnection);

        // Runs once after all tests
        try ztf.afterAll(alloc, teardownDatabase);

        try ztf.it(alloc, "should query data", testQuery);
        try ztf.it(alloc, "should insert data", testInsert);
    }

    fn setupDatabase(alloc: std.mem.Allocator) !void {
        // Initialize database
    }

    fn teardownDatabase(alloc: std.mem.Allocator) !void {
        // Cleanup database
    }

    fn openConnection(alloc: std.mem.Allocator) !void {
        // Open DB connection
    }

    fn closeConnection(alloc: std.mem.Allocator) !void {
        // Close DB connection
    }

    fn testQuery(alloc: std.mem.Allocator) !void {
        // Test implementation
    }

    fn testInsert(alloc: std.mem.Allocator) !void {
        // Test implementation
    }
}.testSuite);
pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer * = gpa.deinit();
    const allocator = gpa.allocator();

    // Define your test suites...
    try ztf.describe(allocator, "My tests", ...);

    // Run tests
    const registry = ztf.getRegistry(allocator);
    const success = try ztf.runTests(allocator, registry);

    // Clean up - IMPORTANT!
    ztf.cleanupRegistry();

    if (!success) {
        std.process.exit(1);
    }
}
try ztf.describe(allocator, "User Service", struct {
    fn testSuite(alloc: std.mem.Allocator) !void {
        try ztf.describe(alloc, "Authentication", struct {
            fn nestedSuite(nested*alloc: std.mem.Allocator) !void {
                try ztf.it(nested*alloc, "should login with valid credentials", testValidLogin);
                try ztf.it(nested*alloc, "should reject invalid credentials", testInvalidLogin);
            }

            fn testValidLogin(nested*alloc: std.mem.Allocator) !void {
                // Test implementation
            }

            fn testInvalidLogin(nested*alloc: std.mem.Allocator) !void {
                // Test implementation
            }
        }.nestedSuite);
    }
}.testSuite);
// Create a mock
var mock*fn = ztf.createMock(alloc, i32);
defer mock*fn.deinit();

// Record calls and assert
try mock*fn.recordCall("arg1");
try mock*fn.toHaveBeenCalled();
try mock*fn.toHaveBeenCalledTimes(1);
try mock*fn.toHaveBeenCalledWith("arg1");

// Mock return values

* = try mock*fn.mockReturnValue(42);
* = try mock*fn.mockReturnValueOnce(100);

const value = mock*fn.getReturnValue(); // Returns 100 first time, then 42

// Spy on existing functions
const original: i32 = 99;
var spy = ztf.createSpy(alloc, i32, original);
defer spy.deinit();

try spy.call("test");
try spy.toHaveBeenCalled();
const restored = spy.mockRestore();
// Create a snapshot
var snap = ztf.createSnapshot(alloc, "user*test", .{
    .update = true,
    .format = .json,
});

// Match against snapshot
const user = User{ .name = "Alice", .age = 30 };
try snap.match(user);

// Named snapshots
try snap.matchNamed("initial*state", initial);
try snap.matchNamed("after*update", updated);

// String snapshots
try snap.matchString("Expected output");
// Set specific time (Jan 1, 2020)
ztf.setSystemTime(alloc, 1577836800000);

const year = ztf.DateHelper.getYear(ztf.time.now(alloc));
try ztf.expect(alloc, year).toBe(@as(u16, 2020));

// Advance time
ztf.advanceTimersByTime(alloc, 60000); // Advance 1 minute

// Jest-compatible API
ztf.jest.useFakeTimers(alloc);
ztf.jest.setSystemTime(alloc, 1577836800000);
const current = ztf.jest.now(alloc);
ztf.jest.advanceTimersByTime(alloc, 5000);
ztf.jest.useRealTimers(alloc);

// Reset to real time
ztf.setSystemTime(alloc, null);
// Skip a test
try ztf.itSkip(alloc, "should skip this test", testSkipped);

// Skip an entire suite
try ztf.describeSkip(allocator, "Skipped suite", struct {
    // All tests in this suite will be skipped
}.testSuite);

// Run only specific tests
try ztf.itOnly(alloc, "should run only this test", testOnly);

// Run only specific suites
try ztf.describeOnly(allocator, "Only this suite", struct {
    // Only tests in this suite will run
}.testSuite);
// Basic async test
try ztf.itAsync(allocator, "async operation", struct {
    fn run(alloc: std.mem.Allocator) !void {

        * = alloc;

        std.Thread.sleep(100 * std.time.ns*per*ms);
        // Test async operations
    }
}.run);

// Async test with custom timeout
try ztf.itAsyncTimeout(allocator, "slow async operation", asyncTestFn, 10000);

// Skip/only for async tests
try ztf.itAsyncSkip(allocator, "skipped async test", testFn);
try ztf.itAsyncOnly(allocator, "focused async test", testFn);

// Using AsyncTestExecutor for advanced control
var executor = ztf.AsyncTestExecutor.init(allocator, .{
    .concurrent = true,
    .max*concurrent = 5,
    .default*timeout*ms = 5000,
});
defer executor.deinit();

try executor.registerTest("test1", testFn1);
try executor.registerTest("test2", testFn2);

const results = try executor.executeAll();
defer allocator.free(results);
// Per-test timeout (1 second)
try ztf.itTimeout(allocator, "timed test", testFn, 1000);

// Per-suite timeout (5 seconds for all tests in suite)
try ztf.describeTimeout(allocator, "Timed Suite", 5000, struct {
    fn suite(alloc: std.mem.Allocator) !void {
        try ztf.it(alloc, "test 1", test1);
        try ztf.it(alloc, "test 2", test2);
    }
}.suite);

// Global timeout configuration
const global*config = ztf.GlobalTimeoutConfig{
    .default*timeout*ms = 5000,
    .enabled = true,
    .allow*extension = true,
    .max*extension*ms = 30000,
};

var enforcer = ztf.TimeoutEnforcer.init(allocator, global*config);
defer enforcer.deinit();

// Timeout context for manual control
var context = ztf.TimeoutContext.init(allocator, 1000);
context.start();

// Extend timeout if needed
try context.extend(500);

// Check status
if (context.isTimedOut()) {
    // Handle timeout
}

context.complete();
var result = try context.getResult();
defer result.deinit();