Quality assurance tools based on std.testing and std.debug (Zig 0.16.0).
Capability Boundaries
✅ Strengths
- Writing unit tests and assertions
- Test organization (test declarations, doctests, test filters)
- Memory leak detection
- Log output (std.log)
- Debugging tools (panic, assertions, stack traces, hex dump)
⚠️ Prerequisites
- Confirm the Zig version (
zig version)
❌ Out of Scope
- Build system test configuration → use the
zig-build-systemskill - Integration testing frameworks → use the
zig-0.16skill - Performance benchmarking → not covered for now (Zig does not yet have a built-in benchmark)
When to Use
- "Help me write unit tests"
- "Debug the output of this function"
- "Check for memory leaks"
Data Privacy
This skill does not collect, store, or transmit any user data.
Workflow — Testing
Step 1. Declare the test — test "name" { ... }
Step 2. Use assertions — expect, expectEqual, expectEqualStrings
Step 3. Run the tests — zig test src/main.zig or zig build test
Step 4. Check the results — pass/fail/leak
Assertion Quick Reference
| Assertion | Purpose |
|---|---|
expect(bool) |
The condition is true |
expectEqual(expected, actual) |
Shallow equality (peer type) |
expectEqualDeep(expected, actual) |
Deep equality (recursive comparison) |
expectEqualStrings(expected, actual) |
String equality (with diff) |
expectEqualSlices(T, expected, actual) |
Slice equality |
expectError(error, result) |
Returns a specific error |
expectApproxEqAbs(expected, actual, eps) |
Floating-point approximation (absolute error) |
expectApproxEqRel(expected, actual, eps) |
Floating-point approximation (relative error) |
expectFmt(expected, template, args) |
Formatted output matches |
expectStringStartsWith(actual, prefix) |
String prefix |
expectStringEndsWith(actual, suffix) |
String suffix |
Basic Assertions
test "basic assertions" {
try std.testing.expect(true);
try std.testing.expectEqual(@as(u32, 42), @as(u32, 42));
try std.testing.expectEqualStrings("hello", "hello");
try std.testing.expectEqualSlices(u8, &[_]u8{ 1, 2 }, &[_]u8{ 1, 2 });
try std.testing.expectEqualDeep(
struct{ x: i32, y: i32 }{ .x = 1, .y = 2 },
struct{ x: i32, y: i32 }{ .x = 1, .y = 2 },
);
}
Floating-Point Comparison
test "float comparison" {
try std.testing.expectApproxEqAbs(@as(f32, 1.0), 1.0001, 0.001);
try std.testing.expectApproxEqRel(@as(f64, 100.0), 99.5, 0.01);
}
Error Checking
test "error handling" {
const result: anyerror!i32 = error.SomeError;
try std.testing.expectError(error.SomeError, result);
}
Test Organization
Inline Tests
const std = @import("std");
const expect = std.testing.expect;
test "inline test" {
try expect(1 + 1 == 2);
}
doctest (tests in documentation)
/// Adds two numbers.
///
/// ```
/// const result = add(2, 3);
/// try std.testing.expectEqual(@as(i32, 5), result);
/// ```
fn add(a: i32, b: i32) i32 {
return a + b;
}
Test Filters
# 运行名称包含 "http" 的测试
zig test src/main.zig --test-filter "http"
# 在 build.zig 中过滤
const tests = b.addTest(.{
.root_module = mod,
.filters = &.{"specific_test"},
});
Test Modules
// 在文件末尾使用 test 块
test "module tests" {
// 引用当前模块
_ = @import("main.zig");
// 或者单独引用测试文件
_ = @import("tests/tests.zig");
}
Memory Leak Detection
Using DebugAllocator
test "memory leak detection" {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer {
const leaked = gpa.deinit();
// leaked 为 true 表示有泄漏
if (leaked) @panic("Memory leak detected!");
}
const allocator = gpa.allocator();
const buf = try allocator.alloc(u8, 100);
// ⚠️ 忘记 allocator.free(buf) 会导致泄漏
// defer allocator.free(buf);
}
Using testing.allocator
test "use testing allocator" {
const allocator = std.testing.allocator;
// testing.allocator 会自动检测泄漏
const buf = try allocator.alloc(u8, 10);
defer allocator.free(buf); // 不 free 会报告泄漏
}
Detection Logs
# 测试失败时输出泄漏信息
zig test src/main.zig
# Memory leak detected: 1 allocations remaining (100 bytes)
Logging and Debugging
Leveled Logging
std.log.debug("debug message: {}", .{value}); // 编译时可用
std.log.info("info: {}", .{value}); // 默认显示
std.log.warn("warning: {}", .{value}); // 重要警告
std.log.err("error: {}", .{value}); // 错误
Custom Log Scopes
const log = std.log.scoped(.http_client);
log.info("request to {s}", .{url});
// 输出: [http_client] request to https://example.com
Runtime Log Levels
# 设置日志级别(默认 .info)
zig build -Dlog-level=debug
# 或按作用域过滤
zig build -Dlog-scope-override=http_client=debug
panic and Assertions
// 调试断言(仅在安全模式下检查)
std.debug.assert(x > 0);
// 不可达(标记不可能执行的代码路径)
unreachable;
// 主动 panic
@panic("something went wrong");
Stack Traces
// 在 panic 或断言失败时自动打印堆栈
// 手动打印:
std.debug.dumpCurrentStackTrace(null);
Hex Dump
const bytes = [_]u8{ 0x48, 0x65, 0x6C, 0x6C, 0x6F };
std.debug.hexDump("data", &bytes);
// 输出:
// data:
// 00000000: 48 65 6C 6C 6F Hello
Test Configuration (build.zig)
Standard Test Step
const tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
const run_tests = b.addRunArtifact(tests);
const test_step = b.step("test", "运行单元测试");
test_step.dependOn(&run_tests.step);
Multi-Module Test Aggregation
const test_step = b.step("test", "运行所有测试");
for ([_]*std.Build.Module{ mod_a, mod_b, mod_c }) |mod| {
const t = b.addTest(.{ .root_module = mod });
test_step.dependOn(&b.addRunArtifact(t).step);
}
Gotchas
expectEqualUse peer type — the two parameters must have explicitly matching types; recommendedexpectEqual(@as(u32, 42), value)testing.allocatoris only for testing — it automatically detects leaks, but performance is poor; in production useDebugAllocator- test declarations cannot be nested —
test "..." { test "..." {} }is invalid - doctests only verify and do not output — code blocks in documentation are tested by default, but if they panic, detailed information is not displayed
std.log.debugis not displayed by default — it needs to be enabled at build time-Dlog-level=debug
FAQ
Q: How do I run only specific tests?
A: Command line zig test src/main.zig --test-filter "http", or via build.zig's filters option.
Q: How do I write the initialization code that the tests depend on?
A: Initialize inside the test block, or create a global test helper function fn testUtil() !void。
Q: Does Zig have benchmarks?
A: The Zig standard library does not yet have a built-in benchmark tool. You can time it manually in tests:const start = std.time.Timer.start()?。