Introdução
Testes em Zig são cidadãos de primeira classe — integrados diretamente na linguagem com o bloco test. Não há necessidade de framework externo. Os testes ficam no mesmo arquivo que o código, são executados com zig test, e aproveitam o testing.allocator para detectar vazamentos de memória automaticamente.
Para testes avançados, veja Testes com Allocator, Test Expectations e Mocking e Stubbing.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
Teste Básico
const std = @import("std");
fn soma(a: i32, b: i32) i32 {
return a + b;
}
fn fatorial(n: u32) u64 {
if (n == 0) return 1;
var resultado: u64 = 1;
var i: u32 = 1;
while (i <= n) : (i += 1) {
resultado *= i;
}
return resultado;
}
test "soma básica" {
try std.testing.expectEqual(@as(i32, 5), soma(2, 3));
try std.testing.expectEqual(@as(i32, 0), soma(0, 0));
try std.testing.expectEqual(@as(i32, -1), soma(2, -3));
}
test "fatorial" {
try std.testing.expectEqual(@as(u64, 1), fatorial(0));
try std.testing.expectEqual(@as(u64, 1), fatorial(1));
try std.testing.expectEqual(@as(u64, 120), fatorial(5));
try std.testing.expectEqual(@as(u64, 3628800), fatorial(10));
}
Executar Testes
# Testar um arquivo
zig test src/main.zig
# Testar com output detalhado
zig test src/main.zig --summary all
# Testar via build system
zig build test
Assertions Disponíveis
const testing = std.testing;
test "assertions" {
// Igualdade
try testing.expectEqual(@as(i32, 42), resultado);
// Strings
try testing.expectEqualStrings("esperado", obtido);
// Slices
try testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, slice);
// Booleano
try testing.expect(condicao_verdadeira);
// Aproximação (floats)
try testing.expectApproxEqAbs(@as(f64, 3.14), pi, 0.01);
// Erro esperado
try testing.expectError(error.DivisaoPorZero, dividir(1, 0));
// Formato
try testing.expectFmt("42", "{}", .{@as(i32, 42)});
}
Testar Funções que Retornam Erros
fn dividir(a: f64, b: f64) !f64 {
if (b == 0) return error.DivisaoPorZero;
return a / b;
}
test "divisão com sucesso" {
const resultado = try dividir(10, 2);
try std.testing.expectEqual(@as(f64, 5.0), resultado);
}
test "divisão por zero retorna erro" {
const resultado = dividir(10, 0);
try std.testing.expectError(error.DivisaoPorZero, resultado);
}
Veja Error Sets Customizados para definir erros testáveis.
Organização de Testes
Testes no Mesmo Arquivo
// src/calculadora.zig
pub fn soma(a: i32, b: i32) i32 {
return a + b;
}
pub fn multiplicar(a: i32, b: i32) i32 {
return a * b;
}
// Testes no final do arquivo
test "soma" {
try std.testing.expectEqual(@as(i32, 7), soma(3, 4));
}
test "multiplicar" {
try std.testing.expectEqual(@as(i32, 12), multiplicar(3, 4));
}
Testes em Arquivo Separado
// tests/calculadora_test.zig
const std = @import("std");
const calc = @import("../src/calculadora.zig");
test "soma de números negativos" {
try std.testing.expectEqual(@as(i32, -5), calc.soma(-2, -3));
}
Bloco de Testes Agrupados
test "operações com strings" {
// Teste 1: concatenação
{
// ...
}
// Teste 2: split
{
// ...
}
}
Testes com Setup e Teardown
fn criarAmbiente(allocator: std.mem.Allocator) !*Ambiente {
const env = try allocator.create(Ambiente);
env.* = try Ambiente.init(allocator);
return env;
}
test "teste com setup" {
const allocator = std.testing.allocator;
// Setup
const env = try criarAmbiente(allocator);
defer {
env.deinit();
allocator.destroy(env);
}
// Teste
try env.executar();
try std.testing.expect(env.resultado == .sucesso);
}
Pular Testes Condicionalmente
const builtin = @import("builtin");
test "apenas no linux" {
if (builtin.os.tag != .linux) return error.SkipZigTest;
// Teste específico de Linux
}
test "apenas em debug" {
if (builtin.mode != .Debug) return error.SkipZigTest;
// Teste que só faz sentido em debug
}
Testes com Allocator de Teste
test "função não vaza memória" {
// testing.allocator detecta leaks automaticamente
const resultado = try minhaFuncao(std.testing.allocator);
defer std.testing.allocator.free(resultado);
try std.testing.expect(resultado.len > 0);
}
// Se houver leak, o teste FALHA com mensagem clara
Veja Testes com Allocator para mais detalhes.
Integrar com build.zig
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Executável principal
const exe = b.addExecutable(.{
.name = "meu-projeto",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
// Testes
const testes = b.addTest(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
const run_testes = b.addRunArtifact(testes);
const test_step = b.step("test", "Executar testes unitários");
test_step.dependOn(&run_testes.step);
}
Conclusão
Testes em Zig são simples, integrados e poderosos. Escreva testes junto com o código, use std.testing.allocator para detectar leaks, e execute com zig test. Para padrões mais avançados, consulte Testes com Allocator, Test Expectations e Benchmarking.