Cheatsheet: Testing em Zig
Zig tem suporte nativo a testes integrado diretamente na linguagem. Não é necessário nenhum framework externo — você escreve blocos test no mesmo arquivo do código e o compilador cuida do resto. Isso incentiva a prática de testes desde o primeiro momento.
Teste Básico
const std = @import("std");
const expect = std.testing.expect;
fn somar(a: i32, b: i32) i32 {
return a + b;
}
test "somar dois números positivos" {
try expect(somar(2, 3) == 5);
}
test "somar com zero" {
try expect(somar(0, 42) == 42);
}
test "somar números negativos" {
try expect(somar(-1, -1) == -2);
}
Executar:
zig test arquivo.zig
Funções de Asserção
expect — Verificação booleana
const expect = std.testing.expect;
test "expect básico" {
try expect(true);
try expect(1 + 1 == 2);
try expect("abc".len == 3);
}
expectEqual — Comparação de igualdade
const expectEqual = std.testing.expectEqual;
test "expectEqual" {
try expectEqual(@as(i32, 42), somar(40, 2));
try expectEqual(@as(usize, 3), "abc".len);
}
expectEqualStrings — Comparação de strings
const expectEqualStrings = std.testing.expectEqualStrings;
test "comparar strings" {
const resultado = "Olá, mundo!";
try expectEqualStrings("Olá, mundo!", resultado);
}
expectEqualSlices — Comparação de slices
const expectEqualSlices = std.testing.expectEqualSlices;
test "comparar slices" {
const a = [_]i32{ 1, 2, 3 };
const b = [_]i32{ 1, 2, 3 };
try expectEqualSlices(i32, &a, &b);
}
expectApproxEqAbs — Comparação de floats
const expectApproxEqAbs = std.testing.expectApproxEqAbs;
test "comparar floats com tolerância" {
try expectApproxEqAbs(@as(f64, 3.14159), 3.14160, 0.001);
}
expectError — Verificar que retorna erro
const expectError = std.testing.expectError;
fn dividir(a: i32, b: i32) !i32 {
if (b == 0) return error.DivisaoPorZero;
return @divTrunc(a, b);
}
test "divisão por zero retorna erro" {
try expectError(error.DivisaoPorZero, dividir(10, 0));
}
Tabela de Asserções
| Função | Descrição | Exemplo |
|---|---|---|
expect(bool) | Verifica condição booleana | try expect(x > 0) |
expectEqual(esperado, obtido) | Igualdade exata | try expectEqual(42, f()) |
expectEqualStrings(a, b) | Igualdade de strings | try expectEqualStrings("ab", s) |
expectEqualSlices(T, a, b) | Igualdade de slices | try expectEqualSlices(u8, &a, &b) |
expectApproxEqAbs(a, b, tol) | Float com tolerância absoluta | try expectApproxEqAbs(3.14, pi, 0.01) |
expectApproxEqRel(a, b, tol) | Float com tolerância relativa | try expectApproxEqRel(100.0, x, 0.01) |
expectError(err, expr) | Verifica erro específico | try expectError(error.X, f()) |
expectFmt(esperado, fmt, args) | Verifica saída formatada | try expectFmt("42", "{d}", .{42}) |
Allocator de Teste
O std.testing.allocator detecta vazamentos de memória automaticamente — se o teste terminar com memória não liberada, o teste falha.
const std = @import("std");
test "sem vazamento de memória" {
const allocator = std.testing.allocator;
var lista = std.ArrayList(u8).init(allocator);
defer lista.deinit(); // Se esquecer, o teste FALHA
try lista.append('a');
try lista.append('b');
try lista.append('c');
try std.testing.expectEqual(@as(usize, 3), lista.items.len);
}
Teste com Setup e Teardown
Zig não tem beforeEach/afterEach formal, mas você pode usar funções auxiliares:
const std = @import("std");
const Contexto = struct {
allocator: std.mem.Allocator,
buffer: []u8,
fn init(allocator: std.mem.Allocator) !Contexto {
return .{
.allocator = allocator,
.buffer = try allocator.alloc(u8, 1024),
};
}
fn deinit(self: *Contexto) void {
self.allocator.free(self.buffer);
}
};
test "com setup e teardown" {
var ctx = try Contexto.init(std.testing.allocator);
defer ctx.deinit();
// usar ctx.buffer no teste...
ctx.buffer[0] = 42;
try std.testing.expectEqual(@as(u8, 42), ctx.buffer[0]);
}
Testes em Módulos Separados
Testando código de outro arquivo
// Em math.zig
pub fn fatorial(n: u64) u64 {
if (n <= 1) return 1;
return n * fatorial(n - 1);
}
test "fatorial de 5" {
try std.testing.expectEqual(@as(u64, 120), fatorial(5));
}
Referenciando testes de outros módulos
// Em tests.zig — importar para executar testes de múltiplos módulos
const std = @import("std");
// Ao referenciar esses módulos, seus testes também são incluídos
comptime {
_ = @import("math.zig");
_ = @import("utils.zig");
_ = @import("parser.zig");
}
test "teste adicional centralizado" {
// ...
}
Executar todos: zig test tests.zig
Testes Condicionais
Pular testes com return
const builtin = @import("builtin");
test "apenas em Linux" {
if (builtin.os.tag != .linux) return; // pula em outros SOs
// código específico de Linux...
try std.testing.expect(true);
}
test "apenas em modo debug" {
if (builtin.mode != .Debug) return;
// verificações que só fazem sentido em debug
try std.testing.expect(true);
}
Comandos do Test Runner
# Rodar todos os testes de um arquivo
zig test arquivo.zig
# Rodar com filtro de nome
zig test arquivo.zig --test-filter "fatorial"
# Rodar em modo release
zig test arquivo.zig -OReleaseSafe
# Com build.zig
zig build test
# Verbose — ver quais testes rodaram
zig test arquivo.zig --verbose
Testes no build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Compilar o executável principal
const exe = b.addExecutable(.{
.name = "meu-app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
// Configurar testes
const testes = b.addTest(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
const run_testes = b.addRunArtifact(testes);
// Step "test" — executar com `zig build test`
const test_step = b.step("test", "Executar testes unitários");
test_step.dependOn(&run_testes.step);
}
Padrões Úteis para Testes
Teste de tabela (table-driven tests)
test "somar — table driven" {
const casos = .{
.{ 1, 2, 3 },
.{ 0, 0, 0 },
.{ -1, 1, 0 },
.{ 100, -100, 0 },
};
inline for (casos) |caso| {
try std.testing.expectEqual(@as(i32, caso[2]), somar(caso[0], caso[1]));
}
}
Teste com timeout (via build.zig)
// No build.zig
const run_testes = b.addRunArtifact(testes);
run_testes.max_rss = 256 * 1024 * 1024; // limite de 256MB RAM
Erros Comuns em Testes
// ERRO: Esquecer o try nas asserções
test "sem try" {
// expect(true); // ERRADO — ignora o erro
try expect(true); // CORRETO
}
// ERRO: Tipo errado no expectEqual
test "tipos diferentes" {
// expectEqual(42, funcao()); // pode falhar por tipo
try expectEqual(@as(i32, 42), funcao()); // CORRETO — tipo explícito
}
// ERRO: Vazamento de memória com testing.allocator
test "vazamento" {
const alloc = std.testing.allocator;
const buf = try alloc.alloc(u8, 100);
defer alloc.free(buf); // SEM ISSO, o teste falha
}
Veja Também
- Error Handling — Como erros interagem com testes
- Build System — Configuração de testes no build.zig
- Troubleshooting: Teste Falha — Problemas comuns com testes
- CLI do Zig — Opções do
zig test - Receitas — Exemplos práticos com testes