---
title: "Testes em Zig: Unit Tests e std.testing Guia | Zig Brasil"
url: "https://ziglang.com.br/tutoriais/testes-zig/"
markdown_url: "https://ziglang.com.br/tutoriais/testes-zig.MD"
description: "Aprenda testes em Zig: unitários, integração, std.testing, mocks e CI/CD. Guia completo com exemplos práticos para testar código Zig em português."
date: "2026-02-09"
author: ""
---

# Testes em Zig: Unit Tests e std.testing Guia | Zig Brasil

Aprenda testes em Zig: unitários, integração, std.testing, mocks e CI/CD. Guia completo com exemplos práticos para testar código Zig em português.


Testes são fundamentais para garantir a qualidade e confiabilidade do seu código. Zig não apenas facilita a escrita de testes — ela os torna uma parte natural e integrada do workflow de desenvolvimento. Neste guia completo, você vai aprender tudo sobre **testes em Zig**, desde o básico até técnicas avançadas de mocking e integração com CI/CD.

## Por que Testar em Zig?

Antes de mergulharmos na prática, vamos entender por que Zig se destaca quando o assunto é testing:

| Característica | Zig | Outras Linguagens |
|----------------|-----|-------------------|
| **Testes integrados** | `test` blocks no mesmo arquivo | Arquivos separados (geralmente) |
| **Compilação condicional** | `builtin.is_test` nativo | Flags de compilação |
| **Performance** | Código de teste compilado, não interpretado | Interpretado (Python, JS) ou VM (Java) |
| **Mocks** | Comptime + structs flexíveis | Frameworks complexos |
| **Memory safety** | Detecta vazamentos nos testes | Depende da linguagem/runtime |
| **Stack traces** | Detalhados em falhas de teste | Variável por linguagem |

### Vantagens Únicas do Zig Testing

1. **Zero overhead**: Testes são compilados como código nativo — sem interpretação lenta
2. **Testes como documentação**: Blocos `test` ficam junto ao código, servindo como exemplos vivos
3. **Memory leak detection**: O test runner detecta automaticamente vazamentos de memória
4. **Determinístico**: Sem garbage collector causando comportamentos não-determinísticos

## O Módulo `std.testing`

A biblioteca padrão de Zig inclui `std.testing`, que fornece tudo o que você precisa para escrever testes eficazes. Vamos conhecer as principais ferramentas:

### Importando e Estrutura Básica

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

test "descrição do teste" {
    // Seu código de teste aqui
    try testing.expect(true);
}
```

O bloco `test` é uma construção de primeiro nível em Zig — não está dentro de funções, mas sim no nível do arquivo, ao lado de funções, structs e outras declarações.

## Asserts Básicos: O Coração dos Testes

O `std.testing` fornece várias funções de assertion para diferentes cenários:

### `expect`: O Assert Fundamental

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

test "expect básico" {
    const resultado = soma(2, 3);
    try testing.expect(resultado == 5);
}

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

> **Dica**: Sempre use `try` com `testing.expect()` — ela retorna um erro em caso de falha, e `try` propaga isso corretamente.

### `expectEqual`: Comparação de Igualdade

Para valores que você quer comparar diretamente:

```zig
test "expectEqual" {
    const esperado: i32 = 42;
    const atual = calculaResposta();
    
    try testing.expectEqual(esperado, atual);
}

fn calculaResposta() i32 {
    return 42;
}
```

`expectEqual` mostra uma mensagem de erro clara indicando qual valor era esperado e qual foi obtido.

### `expectEqualStrings`: Para Strings

```zig
test "comparação de strings" {
    const nome = "ZigLang";
    try testing.expectEqualStrings("ZigLang", nome);
}
```

### `expectError`: Testando Erros

Quando você quer garantir que uma função retorna um erro específico:

```zig
const ErroDivisao = error{DivisaoPorZero};

fn divide(a: i32, b: i32) !i32 {
    if (b == 0) return ErroDivisao.DivisaoPorZero;
    return @divTrunc(a, b);
}

test "expectError" {
    // Testa que a função RETORNA o erro esperado
    try testing.expectError(ErroDivisao.DivisaoPorZero, divide(10, 0));
    
    // Testa que a função NÃO retorna erro
    const resultado = try divide(10, 2);
    try testing.expectEqual(5, resultado);
}
```

### Outros Asserts Úteis

| Função | Uso |
|--------|-----|
| `expect(value)` | Verifica se booleano é true |
| `expectEqual(expected, actual)` | Compara dois valores |
| `expectEqualStrings(expected, actual)` | Compara strings |
| `expectEqualSlices(T, expected, actual)` | Compara slices |
| `expectError(expected_error, actual_error_union)` | Verifica erro específico |
| `expectApproxEqAbs(expected, actual, tolerance)` | Floats com tolerância absoluta |
| `expectApproxEqRel(expected, actual, tolerance)` | Floats com tolerância relativa |

## Testes Unitários: A Base da Qualidade

Testes unitários verificam funções individuais em isolamento. Em Zig, eles ficam no mesmo arquivo da implementação — uma prática que melhora a manutenibilidade.

### Padrão de Organização

```zig
// math.zig
const std = @import("std");
const testing = std.testing;

// ============ IMPLEMENTAÇÃO ============

pub fn fatorial(n: u32) u64 {
    if (n <= 1) return 1;
    return n * fatorial(n - 1);
}

pub fn ehPrimo(n: u32) bool {
    if (n < 2) return false;
    if (n == 2) return true;
    if (n % 2 == 0) return false;
    
    var i: u32 = 3;
    while (i * i <= n) : (i += 2) {
        if (n % i == 0) return false;
    }
    return true;
}

// ============ TESTES ============

test "fatorial de números pequenos" {
    try testing.expectEqual(1, fatorial(0));
    try testing.expectEqual(1, fatorial(1));
    try testing.expectEqual(2, fatorial(2));
    try testing.expectEqual(6, fatorial(3));
    try testing.expectEqual(24, fatorial(4));
}

test "fatorial de número maior" {
    try testing.expectEqual(120, fatorial(5));
    try testing.expectEqual(3628800, fatorial(10));
}

test "verificação de primos" {
    try testing.expect(!ehPrimo(0));
    try testing.expect(!ehPrimo(1));
    try testing.expect(ehPrimo(2));
    try testing.expect(ehPrimo(3));
    try testing.expect(!ehPrimo(4));
    try testing.expect(ehPrimo(17));
    try testing.expect(!ehPrimo(18));
}
```

### Executando Testes

```bash
# Todos os testes do arquivo
zig test math.zig

# Testes de um pacote inteiro
zig build test

# Testes com mais detalhes
zig test math.zig --test-cmd echo --test-cmd-bin
```

## Testando com Allocators

Um dos superpoderes de Zig é o controle explícito de memória. Nos testes, você deve usar `testing.allocator` — ele automaticamente detecta vazamentos:

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

fn duplicaString(allocator: std.mem.Allocator, s: []const u8) ![]u8 {
    const resultado = try allocator.alloc(u8, s.len);
    @memcpy(resultado, s);
    return resultado;
}

test "duplicaString não vaza memória" {
    const allocator = testing.allocator;
    
    const original = "Hello, Zig!";
    const copia = try duplicaString(allocator, original);
    defer allocator.free(copia);  // Importante: libera a memória
    
    try testing.expectEqualStrings(original, copia);
}

// Este teste FALHARIA se esquecêssemos o defer free:
test "detecta vazamento de memória" {
    const allocator = testing.allocator;
    
    const copia = try duplicaString(allocator, "teste");
    _ = copia;
    // Esquecemos o defer allocator.free(copia)!
    // O teste falhará reportando memory leak
}
```

> **Importante**: `testing.allocator` é um `GeneralPurposeAllocator` configurado para detectar vazamentos. Se você esquecer de liberar memória, o teste falha automaticamente.

## Testes de Integração

Testes de integração verificam múltiplas partes do sistema trabalhando juntas. Em Zig, eles geralmente ficam em um diretório `tests/`.

### Estrutura de Projeto

```
meu-projeto/
├── build.zig
├── src/
│   ├── main.zig
│   └── database.zig
└── tests/
    └── integration_tests.zig
```

### Exemplo de Teste de Integração

```zig
// tests/integration_tests.zig
const std = @import("std");
const testing = std.testing;

// Importa os módulos que queremos testar
const database = @import("../src/database.zig");

const TestContext = struct {
    db: database.Database,
    allocator: std.mem.Allocator,
    
    fn init(allocator: std.mem.Allocator) !TestContext {
        var ctx: TestContext = undefined;
        ctx.allocator = allocator;
        ctx.db = try database.Database.init(allocator, ":memory:");
        return ctx;
    }
    
    fn deinit(self: *TestContext) void {
        self.db.deinit();
    }
};

test "ciclo completo de usuário" {
    var ctx = try TestContext.init(testing.allocator);
    defer ctx.deinit();
    
    // Cria usuário
    const user_id = try ctx.db.createUser(.{
        .name = "João Silva",
        .email = "joao@exemplo.com",
    });
    try testing.expect(user_id > 0);
    
    // Busca usuário
    const user = try ctx.db.getUser(user_id);
    try testing.expectEqualStrings("João Silva", user.name);
    try testing.expectEqualStrings("joao@exemplo.com", user.email);
    
    // Atualiza usuário
    try ctx.db.updateUser(user_id, .{ .name = "João S." });
    const updated = try ctx.db.getUser(user_id);
    try testing.expectEqualStrings("João S.", updated.name);
    
    // Deleta usuário
    try ctx.db.deleteUser(user_id);
    try testing.expectError(error.UserNotFound, ctx.db.getUser(user_id));
}
```

## Mocking em Zig: Simulação Elegante

Mocking (simulação de dependências) é essencial para testar código em isolamento. Zig torna isso elegante usando comptime e interfaces implícitas.

### Padrão de Mock com Comptime

```zig
// http_client.zig
const std = @import("std");

// Define uma interface genérica para HTTP
pub fn HttpClient(comptime T: type) type {
    return struct {
        impl: T,
        
        pub fn get(self: @This(), url: []const u8) ![]u8 {
            return self.impl.get(url);
        }
    };
}

// Implementação real
pub const RealHttpClient = struct {
    allocator: std.mem.Allocator,
    
    pub fn get(self: RealHttpClient, url: []const u8) ![]u8 {
        // Faz requisição HTTP real
        _ = self;
        _ = url;
        return "{"status":"ok"}";
    }
};

// Implementação mock para testes
pub const MockHttpClient = struct {
    responses: std.StringHashMap([]const u8),
    
    pub fn init(allocator: std.mem.Allocator) MockHttpClient {
        return .{
            .responses = std.StringHashMap([]const u8).init(allocator),
        };
    }
    
    pub fn deinit(self: *MockHttpClient) void {
        self.responses.deinit();
    }
    
    pub fn addResponse(self: *MockHttpClient, url: []const u8, response: []const u8) !void {
        try self.responses.put(url, response);
    }
    
    pub fn get(self: MockHttpClient, url: []const u8) ![]u8 {
        return self.responses.get(url) orelse error.NotFound;
    }
};
```

### Usando o Mock nos Testes

```zig
// weather_service.zig
const std = @import("std");
const http = @import("http_client.zig");

pub fn WeatherService(comptime Client: type) type {
    return struct {
        client: http.HttpClient(Client),
        
        pub fn getTemperature(self: @This(), city: []const u8) !f32 {
            const url = try std.fmt.allocPrint(
                self.client.impl.allocator,
                "https://api.weather.com/{s}",
                .{city}
            );
            defer self.client.impl.allocator.free(url);
            
            const response = try self.client.get(url);
            // Parse JSON e extrai temperatura...
            _ = response;
            return 25.5;
        }
    };
}

// ============ TESTES ============
const testing = std.testing;

test "WeatherService com mock" {
    var mock = http.MockHttpClient.init(testing.allocator);
    defer mock.deinit();
    
    try mock.addResponse(
        "https://api.weather.com/SaoPaulo",
        "{\"temp\": 28.5}"
    );
    
    const client = http.HttpClient(http.MockHttpClient){ .impl = mock };
    const service = WeatherService(http.MockHttpClient){ .client = client };
    
    const temp = try service.getTemperature("SaoPaulo");
    try testing.expectApproxEqAbs(25.5, temp, 0.01);
}
```

## Organização de Testes em Projetos Maiores

À medida que seu projeto cresce, você precisa de uma estratégia de organização:

### Estratégia 1: Testes Inline (Pequenos/Médios)

Mantenha testes no mesmo arquivo da implementação para contexto máximo:

```zig
// src/parser.zig
pub fn Parser(comptime T: type) type {
    return struct {
        // implementação...
    };
}

// Testes inline - ideal para unidade
test "Parser.parseNumber" {
    const p = Parser(i32){};
    try testing.expectEqual(42, try p.parseNumber("42"));
}

test "Parser.parseNumber erro" {
    const p = Parser(i32){};
    try testing.expectError(error.InvalidNumber, p.parseNumber("abc"));
}
```

### Estratégia 2: Arquivo de Testes Dedicado (Médios/Grandes)

Para módulos complexos, crie `parser_test.zig`:

```zig
// src/parser_test.zig
const std = @import("std");
const testing = std.testing;
const parser = @import("parser.zig");

// Testes extensos de parser...
```

### Estrutura Recomendada

```
project/
├── build.zig
├── src/
│   ├── main.zig
│   ├── parser.zig           # com testes inline simples
│   ├── database.zig         # com testes inline
│   └── utils.zig            # com testes inline
├── tests/
│   ├── integration/
│   │   ├── api_tests.zig    # testes de API
│   │   └── database_tests.zig
│   └── e2e/
│       └── user_flow_tests.zig
└── test_runner.zig          # configuração customizada (opcional)
```

## Testes e `build.zig`

O sistema de build de Zig facilita a execução de testes em diferentes configurações:

### Configuração Básica

```zig
// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    
    // Compila 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);
    
    // ============ TESTES ============
    
    // Testes unitários
    const unit_tests = b.addTest(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    
    const run_unit_tests = b.addRunArtifact(unit_tests);
    
    // Testes de integração
    const integration_tests = b.addTest(.{
        .root_source_file = b.path("tests/integration_tests.zig"),
        .target = target,
        .optimize = optimize,
    });
    
    const run_integration_tests = b.addRunArtifact(integration_tests);
    
    // Step "test" roda ambos
    const test_step = b.step("test", "Run all tests");
    test_step.dependOn(&run_unit_tests.step);
    test_step.dependOn(&run_integration_tests.step);
    
    // Step "test-unit" só para unitários
    const test_unit_step = b.step("test-unit", "Run unit tests only");
    test_unit_step.dependOn(&run_unit_tests.step);
    
    // Step "test-integration" só para integração
    const test_integration_step = b.step("test-integration", "Run integration tests only");
    test_integration_step.dependOn(&run_integration_tests.step);
}
```

### Executando Testes via Build

```bash
# Todos os testes
zig build test

# Só unitários
zig build test-unit

# Só integração
zig build test-integration

# Com otimização ReleaseFast (para testar performance)
zig build test -Doptimize=ReleaseFast

# Para um target específico
zig build test -Dtarget=aarch64-linux-gnu
```

## CI/CD: Integração Contínua

Automatizar testes em CI/CD garante que seu código sempre funcione. Aqui estão configurações para as principais plataformas:

### GitHub Actions

```yaml
# .github/workflows/test.yml
name: Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        zig-version: [0.13.0, master]
    
    runs-on: ${{ matrix.os }}
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Zig
        uses: goto-bus-stop/setup-zig@v2
        with:
          version: ${{ matrix.zig-version }}
      
      - name: Cache Zig dependencies
        uses: actions/cache@v3
        with:
          path: zig-cache
          key: ${{ runner.os }}-zig-${{ hashFiles('**/build.zig.zon') }}
      
      - name: Run tests
        run: zig build test --summary all
      
      - name: Run tests (ReleaseSafe)
        run: zig build test -Doptimize=ReleaseSafe
      
      - name: Check formatting
        run: zig fmt --check src/
```

### GitLab CI

```yaml
# .gitlab-ci.yml
stages:
  - test
  - build

variables:
  ZIG_VERSION: "0.13.0"

test:zig:
  stage: test
  image: alpine:latest
  before_script:
    - apk add --no-cache curl tar
    - curl -L https://ziglang.org/download/${ZIG_VERSION}/zig-linux-x86_64-${ZIG_VERSION}.tar.xz | tar xJ
    - export PATH=$PWD/zig-linux-x86_64-${ZIG_VERSION}:$PATH
  script:
    - zig build test
    - zig fmt --check src/
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - zig-cache/

test:cross-compile:
  stage: test
  image: alpine:latest
  before_script:
    - apk add --no-cache curl tar
    - curl -L https://ziglang.org/download/${ZIG_VERSION}/zig-linux-x86_64-${ZIG_VERSION}.tar.xz | tar xJ
    - export PATH=$PWD/zig-linux-x86_64-${ZIG_VERSION}:$PATH
  script:
    # Testa cross-compilation para diferentes targets
    - zig build -Dtarget=aarch64-linux-gnu
    - zig build -Dtarget=x86_64-windows-gnu
    - zig build -Dtarget=wasm32-freestanding
```

### Dicas para CI/CD

1. **Teste em múltiplas plataformas**: Linux, macOS, Windows
2. **Teste com diferentes otimizações**: Debug, ReleaseSafe, ReleaseFast
3. **Cross-compilation**: Aproveite o poder de Zig para testar builds cruzados
4. **Formatação**: Inclua `zig fmt --check` para manter consistência
5. **Cache**: Use cache de `zig-cache` para acelerar builds

## Técnicas Avançadas de Teste

### Testando Código Assíncrono

```zig
test "async operation" {
    const frame = async fetchData();
    const result = await frame;
    try testing.expectEqual(200, result.status);
}
```

### Testes Parametrizados com Comptime

```zig
test "parametrized tests" {
    const test_cases = .{
        .{ .input = 0, .expected = 1 },
        .{ .input = 1, .expected = 1 },
        .{ .input = 5, .expected = 120 },
    };
    
    inline for (test_cases) |tc| {
        const result = fatorial(tc.input);
        try testing.expectEqual(tc.expected, result);
    }
}
```

### Testes com Side Effects Controlados

```zig
var test_counter: u32 = 0;

test "conta execuções" {
    test_counter += 1;
    try testing.expect(test_counter > 0);
}
```

## Cobertura de Código

Zig ainda não tem ferramenta nativa de cobertura, mas você pode usar kcov ou llvm-cov:

```bash
# Compila com cobertura
zig test src/main.zig -femit-llvm-ir

# Ou use kcov
kcov /tmp/coverage zig test src/main.zig
```

## Comparação: Zig vs Outras Linguagens

| Aspecto | Zig | Rust | Go | C++ |
|---------|-----|------|-----|-----|
| **Testes inline** | ✅ Sim | ⚠️ Atributos | ✅ Sim | ❌ Arquivos separados |
| **Memory leak detection** | ✅ Automático | ✅ LeakSanitizer | ⚠️ Runtime | ❌ Manual |
| **Test runner built-in** | ✅ Sim | ✅ Sim | ✅ Sim | ❌ Não (precisa framework) |
| **Mocking** | ✅ Comptime | ⚠️ Macros/libs | ✅ Interfaces | ⚠️ Frameworks |
| **Compile-time tests** | ✅ Comptime | ⚠️ Const eval | ❌ Não | ⚠️ Templates |
| **Cross-test (CI)** | ✅ Nativo | ⚠️ Cargo cross | ⚠️ GOOS/GOARCH | ❌ Complexo |

## Boas Práticas e Anti-Padrões

### ✅ Faça

1. **Use `testing.allocator`** para detectar vazamentos automaticamente
2. **Teste os casos de erro** — use `expectError`
3. **Mantenha testes rápidos** — evite I/O lento em testes unitários
4. **Use nomes descritivos** nos blocos `test`
5. **Mock dependências externas** — não faça requisições HTTP reais nos testes
6. **Teste edge cases** — zeros, vazios, máximos, mínimos

### ❌ Não Faça

```zig
// RUIM: Teste sem descrição clara
test "test1" { ... }

// RUIM: Não usar testing.allocator
const allocator = std.heap.page_allocator;

// RUIM: Teste que depende de estado externo
test "usar arquivo real" {
    const file = try std.fs.cwd().openFile("/tmp/data.txt", .{});
    // Pode falhar em CI!
}

// RUIM: Não testar erros
test "só sucesso" {
    const result = divide(10, 2);  // Esqueceu de testar divisão por zero!
}
```

## Padrões Comuns de Teste

### Arrange-Act-Assert (AAA)

```zig
test "segue padrão AAA" {
    // Arrange (Preparar)
    const allocator = testing.allocator;
    const numerador: i32 = 10;
    const denominador: i32 = 2;
    
    // Act (Agir)
    const resultado = divide(numerador, denominador);
    
    // Assert (Verificar)
    try testing.expectEqual(5, resultado);
}
```

### Given-When-Then

```zig
test "given user is authenticated when accessing profile then returns data" {
    // Given
    const auth = MockAuth{ .authenticated = true };
    const service = UserService.init(auth);
    
    // When
    const profile = try service.getProfile(123);
    
    // Then
    try testing.expectEqualStrings("João", profile.name);
}
```

## Resumo: Checklist de Testes em Zig

- [ ] Use `std.testing` e suas funções de expect
- [ ] Use `testing.allocator` para detectar vazamentos
- [ ] Teste casos de sucesso E casos de erro
- [ ] Use mocks para isolar unidades
- [ ] Organize testes inline ou em arquivos dedicados
- [ ] Configure `build.zig` para múltiplos tipos de teste
- [ ] Integre com CI/CD para testes automatizados
- [ ] Teste cross-compilation se relevante
- [ ] Mantenha testes rápidos e determinísticos

## Próximos Passos

Agora que você domina testes em Zig, continue seu aprendizado:

- 📖 [Gerenciamento de Memória em Zig: Allocators Explicados](/tutoriais/gerenciamento-de-memoria-zig/) — Entenda como `testing.allocator` funciona
- 🔧 [Zig Build System: Guia Completo do build.zig](/tutoriais/zig-build-system/) — Aprofunde-se em configurações avançadas de build
- 🐛 [Tratamento de Erros em Zig: Guia Completo](/tutoriais/tratamento-de-erros-em-zig/) — Domine error unions para testes mais robustos
- 🚀 [Comptime em Zig: O Poder da Execução em Tempo de Compilação](/tutoriais/comptime-em-zig/) — Crie mocks elegantes com metaprogramação

Zig, Go e Rust compartilham a filosofia de testes integrados à linguagem. Compare com o <a href="https://golang.com.br/artigos/go-testing/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">pacote testing de Go</a> e os <a href="https://rustlang.com.br/artigos/rust-testes/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">testes integrados de Rust com #[test]</a>.

---

**Gostou deste tutorial?** Compartilhe com outros desenvolvedores e ajude a comunidade Zig Brasil a crescer! Encontrou algum erro ou tem sugestões? Entre em contato ou abra uma issue no nosso repositório.

*Última atualização: 09 de fevereiro de 2026*  
*Versão do Zig: 0.13.0*
