zig-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseZig Testing
Zig 测试
Purpose
用途
Guide agents through Zig's testing system: and , comptime testing patterns, test filters, the test allocator for leak detection, and Zig's built-in fuzz testing introduced in 0.14.
zig build testzig test引导开发者使用Zig的测试系统:与、comptime测试模式、测试过滤器、用于泄漏检测的测试分配器,以及Zig 0.14版本引入的内置模糊测试。
zig build testzig testTriggers
触发场景
- "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
undefinedzig
// 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
undefinedRun 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"
undefinedzig build test -- --test-filter "add"
undefined2. 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 wraps a in test mode and reports leaks at the end of each test:
std.testing.allocatorGeneralPurposeAllocatorzig
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.allocatorGeneralPurposeAllocatorzig
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
undefinedZig 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
undefinedRun 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 for build.zig configuration and test step setup
skills/zig/zig-build-system - Use for comptime evaluation patterns tested via comptime asserts
skills/zig/zig-comptime - Use for libFuzzer/AFL as alternative fuzz frameworks
skills/runtimes/fuzzing - Use for AddressSanitizer with Zig tests
skills/runtimes/sanitizers
- 如需build.zig配置和测试步骤设置,可使用
skills/zig/zig-build-system - 如需通过comptime断言测试comptime评估模式,可使用
skills/zig/zig-comptime - 如需替代模糊测试框架(如libFuzzer/AFL),可使用
skills/runtimes/fuzzing - 如需在Zig测试中使用AddressSanitizer,可使用
skills/runtimes/sanitizers