zig-testing

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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "zig-testing" with this command: npx skills add mohitmishra786/low-level-dev-skills/mohitmishra786-low-level-dev-skills-zig-testing

Zig Testing

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.

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?"

Workflow

  1. Writing and running tests

// 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)); }

Run all tests in a single file

zig test src/math.zig

Run all tests via build system

zig build test

Verbose output

zig build test -- --verbose

Run specific test by name (substring match)

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

  1. build.zig test configuration

// 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);

}

  1. Test allocator — leak detection

The std.testing.allocator wraps a GeneralPurposeAllocator in test mode and reports leaks at the end of each test:

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

}

  1. Testing assertions

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");

  1. Comptime testing

Zig can run tests at comptime — useful for compile-time constants and type-level checks:

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); } }

  1. Fuzz testing (Zig 0.14+)

Zig 0.14 introduced a built-in fuzzer using coverage-guided fuzzing:

// 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 }

Run the fuzzer

zig build fuzz -Dfuzz=fuzz_target

With corpus directory

zig build fuzz -Dfuzz=fuzz_target -- corpus/

The fuzzer generates and saves interesting inputs to corpus/

Crashes are saved as artifacts

Reproduce a specific crash

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

For build.zig fuzz setup:

// 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);

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cmake

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

static-analysis

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

llvm

No summary provided by upstream source.

Repository SourceNeeds Review