zig-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Zig Testing

Zig 测试

Purpose

用途

Guide agents through Zig's testing system:
zig build test
and
zig test
, comptime testing patterns, test filters, the test allocator for leak detection, and Zig's built-in fuzz testing introduced in 0.14.
引导开发者使用Zig的测试系统:
zig build test
zig test
、comptime测试模式、测试过滤器、用于泄漏检测的测试分配器,以及Zig 0.14版本引入的内置模糊测试。

Triggers

触发场景

  • "How do I write and run tests in Zig?"
  • "How do I filter which Zig tests run?"
  • "How do I detect memory leaks in Zig tests?"
  • "How do I write comptime tests in Zig?"
  • "How do I use Zig's built-in fuzzer?"
  • "How do I test a Zig library?"
  • "如何在Zig中编写并运行测试?"
  • "如何筛选要运行的Zig测试?"
  • "如何在Zig测试中检测内存泄漏?"
  • "如何在Zig中编写comptime测试?"
  • "如何使用Zig的内置模糊测试器?"
  • "如何测试Zig库?"

Workflow

操作流程

1. Writing and running tests

1. 编写与运行测试

zig
// src/math.zig
const std = @import("std");
const testing = std.testing;

pub fn add(a: i32, b: i32) i32 {
    return a + b;
}

pub fn divide(a: f64, b: f64) !f64 {
    if (b == 0.0) return error.DivisionByZero;
    return a / b;
}

// Tests live in the same file or a dedicated test file
test "add: basic addition" {
    try testing.expectEqual(@as(i32, 5), add(2, 3));
    try testing.expectEqual(@as(i32, -1), add(2, -3));
}

test "add: identity" {
    try testing.expectEqual(@as(i32, 42), add(42, 0));
}

test "divide: normal case" {
    const result = try divide(10.0, 2.0);
    try testing.expectApproxEqAbs(result, 5.0, 1e-9);
}

test "divide: by zero returns error" {
    try testing.expectError(error.DivisionByZero, divide(1.0, 0.0));
}
bash
undefined
zig
// src/math.zig
const std = @import("std");
const testing = std.testing;

pub fn add(a: i32, b: i32) i32 {
    return a + b;
}

pub fn divide(a: f64, b: f64) !f64 {
    if (b == 0.0) return error.DivisionByZero;
    return a / b;
}

// 测试代码可放在同一文件或专用测试文件中
test "add: 基础加法" {
    try testing.expectEqual(@as(i32, 5), add(2, 3));
    try testing.expectEqual(@as(i32, -1), add(2, -3));
}

test "add: 恒等性" {
    try testing.expectEqual(@as(i32, 42), add(42, 0));
}

test "divide: 正常情况" {
    const result = try divide(10.0, 2.0);
    try testing.expectApproxEqAbs(result, 5.0, 1e-9);
}

test "divide: 除零返回错误" {
    try testing.expectError(error.DivisionByZero, divide(1.0, 0.0));
}
bash
undefined

Run all tests in a single file

运行单个文件中的所有测试

zig test src/math.zig
zig test src/math.zig

Run all tests via build system

通过构建系统运行所有测试

zig build test
zig build test

Verbose output

详细输出模式

zig build test -- --verbose
zig build test -- --verbose

Run specific test by name (substring match)

通过名称运行特定测试(子字符串匹配)

zig build test -- --test-filter "add"
undefined
zig build test -- --test-filter "add"
undefined

2. build.zig test configuration

2. build.zig 测试配置

zig
// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // Unit test step
    const unit_tests = b.addTest(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    const run_unit_tests = b.addRunArtifact(unit_tests);

    // Integration tests (separate executable)
    const integration_tests = b.addTest(.{
        .root_source_file = b.path("tests/integration.zig"),
        .target = target,
        .optimize = optimize,
    });
    const run_integration = b.addRunArtifact(integration_tests);

    // `zig build test` runs both
    const test_step = b.step("test", "Run all tests");
    test_step.dependOn(&run_unit_tests.step);
    test_step.dependOn(&run_integration.step);

    // `zig build test-unit` runs only unit tests
    const unit_step = b.step("test-unit", "Run unit tests");
    unit_step.dependOn(&run_unit_tests.step);
}
zig
// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // 单元测试步骤
    const unit_tests = b.addTest(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    const run_unit_tests = b.addRunArtifact(unit_tests);

    // 集成测试(独立可执行文件)
    const integration_tests = b.addTest(.{
        .root_source_file = b.path("tests/integration.zig"),
        .target = target,
        .optimize = optimize,
    });
    const run_integration = b.addRunArtifact(integration_tests);

    // `zig build test` 会运行所有测试
    const test_step = b.step("test", "运行所有测试");
    test_step.dependOn(&run_unit_tests.step);
    test_step.dependOn(&run_integration.step);

    // `zig build test-unit` 仅运行单元测试
    const unit_step = b.step("test-unit", "运行单元测试");
    unit_step.dependOn(&run_unit_tests.step);
}

3. Test allocator — leak detection

3. 测试分配器 — 泄漏检测

The
std.testing.allocator
wraps a
GeneralPurposeAllocator
in test mode and reports leaks at the end of each test:
zig
const std = @import("std");
const testing = std.testing;

test "ArrayList: no leaks" {
    // testing.allocator detects leaks and reports them
    var list = std.ArrayList(u32).init(testing.allocator);
    defer list.deinit();   // MUST defer to return memory

    try list.append(1);
    try list.append(2);
    try list.append(3);

    try testing.expectEqual(@as(usize, 3), list.items.len);
    // If you forget defer list.deinit(), test reports a leak
}

test "custom allocation" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer {
        const leaked = gpa.deinit();
        // .ok means no leaks; .leak means memory was not freed
        testing.expect(leaked == .ok) catch @panic("memory leaked!");
    }
    const allocator = gpa.allocator();

    const buf = try allocator.alloc(u8, 1024);
    defer allocator.free(buf);  // leak if forgotten
}
std.testing.allocator
在测试模式下封装了
GeneralPurposeAllocator
,会在每个测试结束时报告内存泄漏:
zig
const std = @import("std");
const testing = std.testing;

test "ArrayList: 无泄漏" {
    // testing.allocator 会检测并报告泄漏
    var list = std.ArrayList(u32).init(testing.allocator);
    defer list.deinit();   // 必须通过defer释放内存

    try list.append(1);
    try list.append(2);
    try list.append(3);

    try testing.expectEqual(@as(usize, 3), list.items.len);
    // 如果忘记defer list.deinit(),测试会报告内存泄漏
}

test "自定义内存分配" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer {
        const leaked = gpa.deinit();
        // .ok表示无泄漏;.leak表示内存未释放
        testing.expect(leaked == .ok) catch @panic("内存泄漏!");
    }
    const allocator = gpa.allocator();

    const buf = try allocator.alloc(u8, 1024);
    defer allocator.free(buf);  // 忘记的话会导致泄漏
}

4. Testing assertions

4. 测试断言

zig
const testing = std.testing;

// Equality
try testing.expectEqual(expected, actual);
try testing.expectEqualStrings("hello", result_str);
try testing.expectEqualSlices(u8, expected_slice, actual_slice);

// Approximate equality (for floats)
try testing.expectApproxEqAbs(expected, actual, tolerance);
try testing.expectApproxEqRel(expected, actual, tolerance);

// Errors
try testing.expectError(error.MyError, might_fail());
try testing.expect(condition);    // basic boolean assertion

// Comparison
try testing.expect(a < b);
try testing.expectStringStartsWith(str, "prefix");
try testing.expectStringEndsWith(str, "suffix");
zig
const testing = std.testing;

// 相等性断言
try testing.expectEqual(expected, actual);
try testing.expectEqualStrings("hello", result_str);
try testing.expectEqualSlices(u8, expected_slice, actual_slice);

// 近似相等(针对浮点数)
try testing.expectApproxEqAbs(expected, actual, tolerance);
try testing.expectApproxEqRel(expected, actual, tolerance);

// 错误断言
try testing.expectError(error.MyError, might_fail());
try testing.expect(condition);    // 基础布尔断言

// 比较断言
try testing.expect(a < b);
try testing.expectStringStartsWith(str, "前缀");
try testing.expectStringEndsWith(str, "后缀");

5. Comptime testing

5. Comptime 测试

Zig can run tests at comptime — useful for compile-time constants and type-level checks:
zig
const std = @import("std");
const testing = std.testing;

// Test comptime functions
fn isPowerOfTwo(n: comptime_int) bool {
    return n > 0 and (n & (n - 1)) == 0;
}

// Comptime assert (compile error if false)
comptime {
    std.debug.assert(isPowerOfTwo(16));
    std.debug.assert(!isPowerOfTwo(15));
    std.debug.assert(isPowerOfTwo(1024));
}

// Test with comptime-known values (runs at comptime in test mode)
test "isPowerOfTwo: comptime" {
    comptime {
        try testing.expect(isPowerOfTwo(8));
        try testing.expect(!isPowerOfTwo(7));
    }
}

// Type-level testing
test "type properties" {
    // Verify alignment and size at comptime
    comptime {
        try testing.expectEqual(8, @alignOf(u64));
        try testing.expectEqual(4, @sizeOf(u32));
        try testing.expectEqual(true, @typeInfo(u8).Int.signedness == .unsigned);
    }
}
Zig支持在编译时运行测试 — 适用于编译时常量和类型级别的检查:
zig
const std = @import("std");
const testing = std.testing;

// 测试comptime函数
fn isPowerOfTwo(n: comptime_int) bool {
    return n > 0 and (n & (n - 1)) == 0;
}

// Comptime断言(如果为false则触发编译错误)
comptime {
    std.debug.assert(isPowerOfTwo(16));
    std.debug.assert(!isPowerOfTwo(15));
    std.debug.assert(isPowerOfTwo(1024));
}

// 使用编译时已知值测试(在测试模式下于编译时运行)
test "isPowerOfTwo: comptime" {
    comptime {
        try testing.expect(isPowerOfTwo(8));
        try testing.expect(!isPowerOfTwo(7));
    }
}

// 类型级别测试
test "类型属性" {
    // 在编译时验证对齐方式和大小
    comptime {
        try testing.expectEqual(8, @alignOf(u64));
        try testing.expectEqual(4, @sizeOf(u32));
        try testing.expectEqual(true, @typeInfo(u8).Int.signedness == .unsigned);
    }
}

6. Fuzz testing (Zig 0.14+)

6. 模糊测试(Zig 0.14+)

Zig 0.14 introduced a built-in fuzzer using coverage-guided fuzzing:
zig
// fuzz_target.zig
const std = @import("std");

// Fuzz entry point: receives arbitrary bytes
export fn fuzz(input: []const u8) void {
    // Call the function under test with fuzz input
    parseInput(input) catch {};
}

fn parseInput(data: []const u8) !void {
    if (data.len < 4) return error.TooShort;
    const magic = std.mem.readInt(u32, data[0..4], .little);
    if (magic != 0xDEADBEEF) return error.BadMagic;
    // ... more parsing
}
bash
undefined
Zig 0.14引入了基于覆盖率引导的内置模糊测试器:
zig
// fuzz_target.zig
const std = @import("std");

// 模糊测试入口:接收任意字节数据
export fn fuzz(input: []const u8) void {
    // 使用模糊输入调用被测函数
    parseInput(input) catch {};
}

fn parseInput(data: []const u8) !void {
    if (data.len < 4) return error.TooShort;
    const magic = std.mem.readInt(u32, data[0..4], .little);
    if (magic != 0xDEADBEEF) return error.BadMagic;
    // ... 更多解析逻辑
}
bash
undefined

Run the fuzzer

运行模糊测试器

zig build fuzz -Dfuzz=fuzz_target
zig build fuzz -Dfuzz=fuzz_target

With corpus directory

指定语料库目录

zig build fuzz -Dfuzz=fuzz_target -- corpus/
zig build fuzz -Dfuzz=fuzz_target -- corpus/

The fuzzer generates and saves interesting inputs to corpus/

模糊测试器会生成并将有价值的输入保存到corpus/目录

Crashes are saved as artifacts

崩溃案例会保存为工件

Reproduce a specific crash

复现特定崩溃

zig build test-fuzz -- corpus/crash-xxxx

For build.zig fuzz setup:

```zig
// build.zig addition
const fuzz_exe = b.addExecutable(.{
    .name = "fuzz",
    .root_source_file = b.path("src/fuzz_target.zig"),
    .target = target,
    .optimize = .ReleaseSafe,
});
fuzz_exe.root_module.fuzz = true;   // enable fuzzing instrumentation
const fuzz_step = b.step("fuzz", "Run fuzzer");
fuzz_step.dependOn(&b.addRunArtifact(fuzz_exe).step);
zig build test-fuzz -- corpus/crash-xxxx

build.zig 中的模糊测试配置:

```zig
// build.zig 新增内容
const fuzz_exe = b.addExecutable(.{
    .name = "fuzz",
    .root_source_file = b.path("src/fuzz_target.zig"),
    .target = target,
    .optimize = .ReleaseSafe,
});
fuzz_exe.root_module.fuzz = true;   // 启用模糊测试插桩
const fuzz_step = b.step("fuzz", "运行模糊测试器");
fuzz_step.dependOn(&b.addRunArtifact(fuzz_exe).step);

Related skills

相关技能

  • Use
    skills/zig/zig-build-system
    for build.zig configuration and test step setup
  • Use
    skills/zig/zig-comptime
    for comptime evaluation patterns tested via comptime asserts
  • Use
    skills/runtimes/fuzzing
    for libFuzzer/AFL as alternative fuzz frameworks
  • Use
    skills/runtimes/sanitizers
    for AddressSanitizer with Zig tests
  • 如需build.zig配置和测试步骤设置,可使用
    skills/zig/zig-build-system
  • 如需通过comptime断言测试comptime评估模式,可使用
    skills/zig/zig-comptime
  • 如需替代模糊测试框架(如libFuzzer/AFL),可使用
    skills/runtimes/fuzzing
  • 如需在Zig测试中使用AddressSanitizer,可使用
    skills/runtimes/sanitizers