Встроенное тестирование
Пишем тесты прямо в исходном файле и запускаем их встроенной командой.
Блок test — особая конструкция Zig, которая содержит проверочный код прямо в исходном файле; команда
zig testнаходит все такие блоки и выполняет их.
Во многих языках тесты — это отдельный фреймворк, который надо подключать. В Zig тестирование встроено в язык: вы пишете блок test прямо рядом с кодом, который он проверяет, и запускаете zig test. Никаких зависимостей, никакой настройки.
Блок test рядом с кодом
const std = @import("std");
const expect = std.testing.expect;
fn add(a: i32, b: i32) i32 {
return a + b;
}
test "сложение работает" {
try expect(add(2, 3) == 5);
try expect(add(-1, 1) == 0);
}
Блок test "имя" содержит проверки. Функция std.testing.expect принимает bool и возвращает ошибку, если условие ложно — поэтому перед ней пишут try. Имя теста — обычная строка, удобная для отчёта. Тесты живут в том же файле, что и код: их легко держать в синхроне.
Запуск тестов
zig test mymodule.zig
# All 1 tests passed.
Команда zig test компилирует файл в специальный тестовый бинарник, находит все блоки test и выполняет их, выводя результат. В обычной сборке (build-exe) тестовые блоки игнорируются — они не попадают в продакшен-бинарник.
Сравнение значений и ошибок
test "проверка равенства и ошибок" {
try std.testing.expectEqual(@as(i32, 10), add(7, 3));
// ожидаем, что функция вернёт конкретную ошибку:
try std.testing.expectError(error.Empty, parse(""));
}
expectEqual сравнивает два значения и при несовпадении печатает оба — удобнее голого expect. expectError проверяет, что выражение вернуло именно ожидаемую ошибку. Это покрывает и успешные пути, и обработку ошибок.
Тестовый аллокатор ловит утечки
test "нет утечек памяти" {
const alloc = std.testing.allocator; // специальный аллокатор для тестов
const buf = try alloc.alloc(u8, 8);
defer alloc.free(buf); // забудь defer — тест упадёт с сообщением об утечке
}
В тестах доступен std.testing.allocator — аллокатор, который автоматически проверяет на утечки. Если в тесте выделили память и не освободили, тест провалится с указанием утечки. Это превращает тесты ещё и в проверку корректности управления памятью — мощная связка с философией явных аллокаторов.
Как работает под капотом
При zig test компилятор собирает отдельный исполняемый файл, в котором каждый блок test становится функцией, а сгенерированная точка входа по очереди их вызывает. Поскольку тесты — часть языка, а не библиотеки, они имеют доступ к приватным функциям модуля и компилируются в том же контексте, что и код. std.testing.allocator — это обёртка над отладочным аллокатором с проверкой утечек на выходе.
Частые ошибки
Первая — забыть try перед expect: без него ошибка теста не сработает. Вторая — использовать обычный аллокатор вместо std.testing.allocator и упустить проверку утечек. Третья — ждать, что тесты попадут в обычный бинарник: они выполняются только через zig test.
Итог
- Тесты пишут в блоках
test "имя" { ... }прямо в исходном файле. zig test file.zigнаходит и выполняет все тесты; в обычной сборке они игнорируются.expect,expectEqual,expectErrorпроверяют условия, значения и ошибки.std.testing.allocatorловит утечки памяти прямо в тестах.