⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

Running Tests

A test block is a language construct, not a framework. No imports, no registration, no annotations.

const std = @import("std");
const expect = std.testing.expect;

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

test "a test is just a named block" {
    try expect(add(2, 2) == 4);
}

test "expectEqual reports both values on failure" {
    try std.testing.expectEqual(@as(i32, 4), add(2, 2));
}

test "comparing slices" {
    try std.testing.expectEqualStrings("abc", "ab" ++ "c");
    try std.testing.expectEqualSlices(u8, &[_]u8{ 1, 2 }, &[_]u8{ 1, 2 });
}

test "asserting an error is returned" {
    const failing = struct {
        fn f() error{Nope}!void {
            return error.Nope;
        }
    }.f;
    try std.testing.expectError(error.Nope, failing());
}

test "the testing allocator fails the test on a leak" {
    const gpa = std.testing.allocator;
    const buf = try gpa.alloc(u8, 10);
    defer gpa.free(buf); // remove this line and the test fails
    try expect(buf.len == 10);
}

test "skipping" {
    if (@import("builtin").cpu.arch != .wasm32) return error.SkipZigTest;
    try expect(true);
}

Running them

zig test file.zig        # just this file
zig build test           # the test step of a build.zig
zig test file.zig --test-filter "name"

Tests in a file only run if that file is the test root or is reachable from it via @import. A test in a module nobody imports never runs, which is worth knowing when a test you expected to fail stays quiet.

The assertion helpers

HelperUse
expect(bool)general condition
expectEqual(a, b)prints both values on failure
expectEqualStringsstring comparison with a readable diff
expectEqualSlices(T, a, b)slice comparison
expectError(err, expr)asserts a specific error

Prefer expectEqual over expect(a == b): when it fails, it tells you what the values actually were.

Leak detection is on by default

std.testing.allocator fails the test if anything it allocated was not freed. This is not an optional tool you remember to run. Every test that allocates is a leak test:

const buf = try std.testing.allocator.alloc(u8, 10);
defer std.testing.allocator.free(buf);   // omit this and the test fails

Skipping

return error.SkipZigTest marks a test skipped rather than failed. It is the way to handle a test that only applies to some targets.

These pages are the tests

Every snippet in this guide is compiled and run by CI, and the test ones report through this same runner. The output you see when you press Run is the real zig test output, executed in your browser as WebAssembly.