---
title: "Testes em Zig: Guia Completo com Exemplos Práticos"
url: "https://ziglang.com.br/artigos/zig-testes-guia-completo/"
markdown_url: "https://ziglang.com.br/artigos/zig-testes-guia-completo.MD"
description: "Guia completo de testes em Zig: blocos test, std.testing, detecção de leaks, testes de tabela, fuzz testing, mocking e integração com CI/CD. Exemplos práticos."
date: "2026-03-27"
author: ""
---

# Testes em Zig: Guia Completo com Exemplos Práticos

Guia completo de testes em Zig: blocos test, std.testing, detecção de leaks, testes de tabela, fuzz testing, mocking e integração com CI/CD. Exemplos práticos.


Uma das decisões de design mais acertadas da **linguagem Zig** foi incorporar testes como cidadãos de primeira classe. Não existe framework externo, não existe configuração especial — testes vivem no mesmo arquivo que o código que testam e são executados com um único comando. Essa filosofia elimina a fricção que, em outras linguagens, faz desenvolvedores pularem testes.

Neste guia completo, vamos cobrir desde o básico até padrões avançados como fuzz testing e integração com CI/CD.

## Seu Primeiro Teste em Zig

Em Zig, testes são declarados com a palavra-chave `test` seguida de uma string descritiva:

```zig
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" {
    const resultado = somar(2, 3);
    try expect(resultado == 5);
}

test "somar com zero" {
    try expect(somar(0, 42) == 42);
    try expect(somar(42, 0) == 42);
}
```

Execute com:

```bash
zig test arquivo.zig
```

Se todos os testes passarem, a saída será:

```
All 2 tests passed.
```

Simples assim. Sem `main()`, sem imports de frameworks, sem boilerplate.

## Funções de Asserção

O módulo `std.testing` oferece várias funções de asserção:

### expect e expectEqual

```zig
const testing = std.testing;

test "asserções básicas" {
    // Condição booleana
    try testing.expect(true);

    // Igualdade com mensagem de erro informativa
    try testing.expectEqual(@as(i32, 42), somar(40, 2));

    // Igualdade aproximada para floats
    try testing.expectApproxEqAbs(@as(f64, 3.14), 3.14159, 0.01);

    // Comparação de strings
    try testing.expectEqualStrings("hello", "hello");

    // Comparação de slices
    try testing.expectEqualSlices(u8, &[_]u8{ 1, 2, 3 }, &[_]u8{ 1, 2, 3 });
}
```

### expectError: Testando Error Unions

Zig tem um sistema de [error handling](/artigos/zig-error-handling-boas-praticas/) poderoso, e testá-lo é igualmente expressivo:

```zig
const DivisionError = error{DivisionByZero};

fn dividir(a: f64, b: f64) DivisionError!f64 {
    if (b == 0.0) return error.DivisionByZero;
    return a / b;
}

test "divisão normal" {
    const resultado = try dividir(10.0, 2.0);
    try testing.expectEqual(@as(f64, 5.0), resultado);
}

test "divisão por zero retorna erro" {
    const resultado = dividir(10.0, 0.0);
    try testing.expectError(error.DivisionByZero, resultado);
}
```

## Detecção de Memory Leaks com Testing Allocator

Este é um dos recursos mais poderosos de testes em Zig. O `std.testing.allocator` detecta automaticamente memory leaks — se qualquer memória alocada não for liberada ao final do teste, o teste **falha**:

```zig
test "sem memory leaks" {
    const allocator = std.testing.allocator;

    var lista = std.ArrayList(u8).init(allocator);
    defer lista.deinit(); // Se esquecer isso, o teste falha!

    try lista.append(42);
    try lista.append(100);

    try testing.expectEqual(@as(usize, 2), lista.items.len);
}

test "detecta leak intencional" {
    const allocator = std.testing.allocator;

    // Isso FALHARÁ — memória alocada mas nunca liberada
    const ptr = try allocator.alloc(u8, 100);
    _ = ptr;
    // Esqueceu: allocator.free(ptr);
}
```

Para quem vem de linguagens com garbage collector, isso pode parecer tedioso. Mas na prática, o [allocator](/glossario/allocator/) de testes encontra bugs de memória que em C levariam horas com Valgrind. Veja nosso artigo sobre [estratégias de alocação de memória](/artigos/zig-alocacao-memoria-estrategias/) para aprofundar.

## Testes Table-Driven

O padrão de table-driven tests, popular em Go, é igualmente elegante em Zig:

```zig
test "fibonacci - table driven" {
    const TestCase = struct {
        input: u32,
        expected: u32,
    };

    const cases = [_]TestCase{
        .{ .input = 0, .expected = 0 },
        .{ .input = 1, .expected = 1 },
        .{ .input = 2, .expected = 1 },
        .{ .input = 5, .expected = 5 },
        .{ .input = 10, .expected = 55 },
        .{ .input = 20, .expected = 6765 },
    };

    for (cases) |tc| {
        const resultado = fibonacci(tc.input);
        try testing.expectEqual(tc.expected, resultado);
    }
}
```

Esse padrão é excelente para testar funções puras com múltiplas entradas. Se você conhece table-driven tests do Go, vai se sentir em casa — e pode comparar as abordagens no <a href="https://golang.com.br" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go Lang Brasil</a>.

## Organização de Testes

### Testes no Mesmo Arquivo

A convenção em Zig é colocar testes no mesmo arquivo que o código:

```zig
// math.zig
pub fn fatorial(n: u32) u32 {
    if (n <= 1) return 1;
    return n * fatorial(n - 1);
}

// Testes ficam no final do arquivo
test "fatorial de 0" {
    try testing.expectEqual(@as(u32, 1), fatorial(0));
}

test "fatorial de 5" {
    try testing.expectEqual(@as(u32, 120), fatorial(5));
}
```

### Executando Testes Específicos

Use `--test-filter` para executar apenas testes que contenham uma substring no nome:

```bash
# Apenas testes com "fatorial" no nome
zig test math.zig --test-filter "fatorial"

# Testes de um módulo específico via build system
zig build test --test-filter "divisão"
```

### Testes com Build System

Para projetos maiores, configure testes no `build.zig`:

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

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,
    });

    // Testes unitários
    const unit_tests = b.addTest(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    const run_tests = b.addRunArtifact(unit_tests);
    const test_step = b.step("test", "Executar testes unitários");
    test_step.dependOn(&run_tests.step);

    b.installArtifact(exe);
}
```

Depois basta rodar:

```bash
zig build test
```

## Testando Código Comptime

Uma particularidade de Zig: você pode testar código que roda em [compile-time](/glossario/comptime/):

```zig
fn comptimeFibonacci(comptime n: u32) u32 {
    if (n <= 1) return n;
    return comptimeFibonacci(n - 1) + comptimeFibonacci(n - 2);
}

test "fibonacci em comptime" {
    // Este cálculo é feito inteiramente em tempo de compilação
    comptime {
        const result = comptimeFibonacci(10);
        if (result != 55) @compileError("fibonacci(10) deveria ser 55");
    }

    // Também funciona com expect normal
    try testing.expectEqual(@as(u32, 55), comptime comptimeFibonacci(10));
}
```

Para mais sobre comptime, veja nosso artigo dedicado: [Comptime em Zig: Metaprogramação sem Macros](/artigos/comptime-zig-metaprogramacao/).

## Mocking e Injeção de Dependência

Zig não tem um framework de mocking embutido, mas o design da linguagem facilita injeção de dependência via parâmetros de tipo e interfaces:

```zig
// Interface via comptime
fn HttpClient(comptime Impl: type) type {
    return struct {
        impl: Impl,

        pub fn get(self: *@This(), url: []const u8) ![]const u8 {
            return self.impl.get(url);
        }
    };
}

// Mock para testes
const MockHttp = struct {
    response: []const u8,

    pub fn get(self: *MockHttp, _: []const u8) ![]const u8 {
        return self.response;
    }
};

test "fetch com mock" {
    var mock = MockHttp{ .response = "{\"status\": \"ok\"}" };
    var client = HttpClient(MockHttp){ .impl = mock };

    const response = try client.get("https://api.example.com/data");
    try testing.expectEqualStrings("{\"status\": \"ok\"}", response);
}
```

Esse padrão de polimorfismo em compile-time é uma das forças de Zig — sem vtables, sem overhead de runtime.

## Fuzz Testing

A partir do Zig 0.12+, fuzz testing está disponível nativamente:

```zig
test "fuzz parser de número" {
    // O fuzzer gera inputs automaticamente
    try std.testing.fuzz(.{}, struct {
        fn testOne(input: []const u8) !void {
            // Tenta parsear como número
            _ = std.fmt.parseInt(i64, input, 10) catch return;
            // Se parseou, verifica que é válido
        }
    }.testOne);
}
```

O fuzzer gera milhares de inputs aleatórios buscando crashes, panics ou erros inesperados. É especialmente valioso para testar parsers, serializers e código que processa dados externos. Go também oferece fuzz testing nativo desde o Go 1.18 — para comparar as abordagens, veja o guia de <a href="https://golang.com.br/cheatsheet/testing-debug/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">testing e debugging em Go</a>.

## Integração com CI/CD

Para rodar testes em pipelines de CI/CD, o comando é direto:

```yaml
# Exemplo com Gitea Actions
name: Test
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Zig
        uses: goto-bus-stop/setup-zig@v2
        with:
          version: 0.13.0
      - name: Run tests
        run: zig build test
      - name: Run tests with release optimization
        run: zig build test -Doptimize=ReleaseSafe
```

Uma boa prática é rodar testes tanto em modo Debug (com todas as safety checks) quanto em ReleaseSafe (para encontrar bugs que só aparecem com otimizações).

## Coverage com kcov

O Zig gera binários de teste compatíveis com ferramentas de coverage como `kcov`:

```bash
# Compilar testes como executável
zig test src/main.zig --test-cmd kcov --test-cmd ./coverage --test-cmd-bin

# O relatório HTML estará em ./coverage/
```

Isso permite integrar relatórios de cobertura de código em seu pipeline de CI.

## Boas Práticas para Testes em Zig

1. **Testes perto do código**: mantenha testes no mesmo arquivo — reduz contexto necessário
2. **Use `testing.allocator` sempre**: detecta leaks automaticamente
3. **Testes devem ser rápidos**: evite I/O real; use mocks para network e filesystem
4. **Testes determinísticos**: nunca dependa de tempo real ou dados aleatórios (exceto fuzz)
5. **Nomes descritivos**: `test "multiplicação de matriz 3x3 com identidade"` > `test "test1"`
6. **Table-driven para múltiplas entradas**: evita duplicação de lógica de teste
7. **Teste error paths**: use `expectError` — o caminho de erro é tão importante quanto o feliz
8. **Rode em múltiplos modos**: Debug para safety, ReleaseSafe para otimização

Para validar código com SIMD e processamento vetorial, combine estas práticas com os exemplos do nosso artigo sobre [SIMD em Zig](/artigos/zig-simd-processamento-vetorial/).

## Comparação com Testes em Outras Linguagens

| Feature | Zig | Rust | Go | Python |
|---------|-----|------|----|---------|
| **Testes built-in** | ✅ `test` blocks | ✅ `#[test]` | ✅ `func Test*` | ❌ unittest/pytest |
| **Detecção de leaks** | ✅ testing.allocator | ❌ (precisa Miri) | N/A (GC) | N/A (GC) |
| **Fuzz built-in** | ✅ std.testing.fuzz | ❌ (cargo-fuzz) | ✅ `testing.F` | ❌ (hypothesis) |
| **Coverage** | Via kcov | Via tarpaulin | Via `go test -cover` | Via coverage.py |
| **Tempo de compilação** | Rápido | Lento | Rápido | N/A |

Se você vem do ecossistema Rust e quer comparar abordagens de teste, veja o <a href="https://rustlang.com.br" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust Lang Brasil</a>. Para a abordagem de testing do Go, confira o <a href="https://golang.com.br" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go Lang Brasil</a>. E para quem quer comparar com pytest, visite o <a href="https://python.dev.br" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Python Dev Brasil</a>.

## Conclusão

O sistema de testes de Zig demonstra a filosofia da linguagem: simplicidade sem sacrificar poder. Com blocos `test` integrados, `testing.allocator` para detecção de leaks e fuzz testing nativo, você tem tudo que precisa para escrever software confiável sem dependências externas.

Comece testando funções simples com `expect`, evolua para table-driven tests, e depois explore fuzz testing para inputs não confiáveis. E para consultas rápidas sobre a sintaxe de testes, mantenha nosso [cheatsheet de testing](/cheatsheets/testing/) à mão.

Para se aprofundar em testes de propriedade em Zig, veja nosso artigo sobre [testes de propriedade](/artigos/zig-testes-propriedade/), e explore os [frameworks de testing do ecossistema](/ecossistema/zig-testing-frameworks/) para necessidades mais avançadas.
