# Testing Utilities

### Testing Utilities

Zig has built-in support for tests.

A test is declared with the `test` keyword.

```zig
const std = @import("std");

test "addition" {
    try std.testing.expect(1 + 1 == 2);
}
```

Run the file with:

```sh
zig test main.zig
```

If the condition is true, the test passes.

If the condition is false, the test fails.

`std.testing.expect` checks a boolean expression.

```zig
try std.testing.expect(x == y);
```

It returns an error when the expression is false, so it is usually called with `try`.

A test block can contain ordinary Zig code.

```zig
const std = @import("std");

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

test "add returns the sum" {
    try std.testing.expect(add(3, 4) == 7);
}
```

Tests can be placed in the same file as the code they test.

This is common in Zig.

A more precise comparison uses `expectEqual`.

```zig
const std = @import("std");

test "equal integers" {
    try std.testing.expectEqual(@as(u32, 42), @as(u32, 42));
}
```

The first argument is the expected value.

The second argument is the actual value.

```zig
try std.testing.expectEqual(expected, actual);
```

For slices, use `expectEqualSlices`.

```zig
const std = @import("std");

test "equal slices" {
    const a = [_]u8{ 1, 2, 3 };
    const b = [_]u8{ 1, 2, 3 };

    try std.testing.expectEqualSlices(u8, a[0..], b[0..]);
}
```

The first argument is the element type.

```zig
u8
```

The next two arguments are the slices to compare.

For strings, use `expectEqualStrings`.

```zig
const std = @import("std");

test "equal strings" {
    try std.testing.expectEqualStrings("zig", "zig");
}
```

Tests can also check for errors.

```zig
const std = @import("std");

fn fail() !void {
    return error.BadInput;
}

test "expected error" {
    try std.testing.expectError(error.BadInput, fail());
}
```

This test passes only if `fail()` returns `error.BadInput`.

The testing module also provides an allocator.

```zig
const std = @import("std");

test "array list" {
    var list = std.ArrayList(u8).init(std.testing.allocator);
    defer list.deinit();

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

    try std.testing.expectEqualSlices(u8, &.{ 1, 2 }, list.items);
}
```

`std.testing.allocator` is useful because the test runner can detect leaks.

If allocated memory is not freed, the test reports it.

This makes tests good places to check ownership.

A test can import other declarations from the same file.

```zig
const std = @import("std");

fn isEven(x: u32) bool {
    return x % 2 == 0;
}

test "even numbers" {
    try std.testing.expect(isEven(2));
    try std.testing.expect(!isEven(3));
}
```

Tests are declarations.

They can appear beside functions, structs, constants, and variables.

A file may contain many tests.

```zig
test "one" {
    try std.testing.expect(true);
}

test "two" {
    try std.testing.expect(10 > 3);
}
```

All tests in the file are run by `zig test`.

For larger programs, tests can be run through the build system.

```zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const tests = b.addTest(.{
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = b.graph.host,
        }),
    });

    const run_tests = b.addRunArtifact(tests);

    const test_step = b.step("test", "Run tests");
    test_step.dependOn(&run_tests.step);
}
```

Then run:

```sh
zig build test
```

The test runner builds the test executable and runs it.

A useful pattern is to keep small tests close to the code.

```zig
fn clamp(x: i32, lo: i32, hi: i32) i32 {
    if (x < lo) return lo;
    if (x > hi) return hi;
    return x;
}

test "clamp" {
    try std.testing.expectEqual(@as(i32, 0), clamp(-5, 0, 10));
    try std.testing.expectEqual(@as(i32, 5), clamp(5, 0, 10));
    try std.testing.expectEqual(@as(i32, 10), clamp(15, 0, 10));
}
```

The test describes the contract of the function.

It also provides a small example of use.

Tests should be simple.

They should build values, call functions, and check results.

The best tests make failure easy to locate.

Exercise 14-29. Write a test for a function that returns the larger of two integers.

Exercise 14-30. Write a test that compares two byte slices.

Exercise 14-31. Write a test that expects a specific error.

Exercise 14-32. Use `std.testing.allocator` in a test and free all allocated memory.

