---
title: "Test Patterns em Zig: Mocks, Stubs, DI e Table-Driven Tests"
url: "https://ziglang.com.br/tutoriais/zig-test-patterns/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-test-patterns.MD"
description: "Padroes avancados de teste em Zig. Arrange-Act-Assert, mocks e stubs, dependency injection para testabilidade e table-driven tests. Tutorial completo em portugues."
date: "2026-02-21"
author: ""
---

# Test Patterns em Zig: Mocks, Stubs, DI e Table-Driven Tests

Padroes avancados de teste em Zig. Arrange-Act-Assert, mocks e stubs, dependency injection para testabilidade e table-driven tests. Tutorial completo em portugues.


Escrever testes e facil. Escrever bons testes que sejam manteneiros, rapidos e confiaveis e uma arte. Neste artigo, exploramos padroes avancados de teste em Zig, incluindo Arrange-Act-Assert, mocks usando interfaces de comptime, dependency injection e table-driven tests.

> Continuacao do artigo sobre [fundamentos de unit tests](/tutoriais/zig-testing-avancado/artigo-1-unit-tests-fundamentos/).

## Arrange-Act-Assert (AAA)

O padrao AAA organiza cada teste em tres fases claras:

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

const ContaBancaria = struct {
    saldo: f64,
    titular: []const u8,

    pub fn depositar(self: *ContaBancaria, valor: f64) !void {
        if (valor <= 0) return error.ValorInvalido;
        self.saldo += valor;
    }

    pub fn sacar(self: *ContaBancaria, valor: f64) !void {
        if (valor <= 0) return error.ValorInvalido;
        if (valor > self.saldo) return error.SaldoInsuficiente;
        self.saldo -= valor;
    }
};

test "deposito aumenta saldo" {
    // Arrange
    var conta = ContaBancaria{ .saldo = 100.0, .titular = "Maria" };

    // Act
    try conta.depositar(50.0);

    // Assert
    try std.testing.expectApproxEqAbs(@as(f64, 150.0), conta.saldo, 0.01);
}

test "saque com saldo insuficiente retorna erro" {
    // Arrange
    var conta = ContaBancaria{ .saldo = 100.0, .titular = "Joao" };

    // Act + Assert
    try std.testing.expectError(error.SaldoInsuficiente, conta.sacar(200.0));
}
```

## Dependency Injection para Testabilidade

Em vez de depender de implementacoes concretas, use interfaces via comptime:

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

// Interface: qualquer tipo que implemente "lerTemperatura"
fn SensorCliente(comptime SensorType: type) type {
    return struct {
        sensor: SensorType,
        historico: std.ArrayList(f32),

        const Self = @This();

        pub fn init(sensor: SensorType, allocator: std.mem.Allocator) Self {
            return .{
                .sensor = sensor,
                .historico = std.ArrayList(f32).init(allocator),
            };
        }

        pub fn deinit(self: *Self) void {
            self.historico.deinit();
        }

        pub fn coletar(self: *Self) !f32 {
            const temp = try self.sensor.lerTemperatura();
            try self.historico.append(temp);
            return temp;
        }

        pub fn media(self: *const Self) f32 {
            if (self.historico.items.len == 0) return 0;
            var soma: f32 = 0;
            for (self.historico.items) |v| soma += v;
            return soma / @as(f32, @floatFromInt(self.historico.items.len));
        }
    };
}

// Implementacao real
const SensorReal = struct {
    pub fn lerTemperatura(_: *const SensorReal) !f32 {
        // Ler do hardware real
        return 23.5;
    }
};

// Mock para testes
const SensorMock = struct {
    valores: []const f32,
    idx: usize = 0,

    pub fn lerTemperatura(self: *const SensorMock) !f32 {
        if (self.idx >= self.valores.len) return error.SemDados;
        const v = self.valores[self.idx];
        // Nota: para mock mutavel, use *SensorMock
        return v;
    }
};

test "sensor cliente com mock" {
    const valores = [_]f32{ 20.0, 22.0, 24.0 };
    var mock = SensorMock{ .valores = &valores };
    var cliente = SensorCliente(SensorMock).init(mock, std.testing.allocator);
    defer cliente.deinit();

    const temp = try cliente.coletar();
    try std.testing.expectApproxEqAbs(@as(f32, 20.0), temp, 0.1);
}
```

## Table-Driven Tests

Table-driven tests sao ideais para testar muitas combinacoes de entrada/saida:

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

fn classificarIMC(imc: f32) []const u8 {
    if (imc < 18.5) return "Abaixo do peso";
    if (imc < 25.0) return "Peso normal";
    if (imc < 30.0) return "Sobrepeso";
    return "Obeso";
}

test "classificacao de IMC" {
    const TestCase = struct {
        imc: f32,
        esperado: []const u8,
    };

    const casos = [_]TestCase{
        .{ .imc = 15.0, .esperado = "Abaixo do peso" },
        .{ .imc = 18.4, .esperado = "Abaixo do peso" },
        .{ .imc = 18.5, .esperado = "Peso normal" },
        .{ .imc = 22.0, .esperado = "Peso normal" },
        .{ .imc = 24.9, .esperado = "Peso normal" },
        .{ .imc = 25.0, .esperado = "Sobrepeso" },
        .{ .imc = 29.9, .esperado = "Sobrepeso" },
        .{ .imc = 30.0, .esperado = "Obeso" },
        .{ .imc = 40.0, .esperado = "Obeso" },
    };

    for (casos) |caso| {
        const resultado = classificarIMC(caso.imc);
        try std.testing.expectEqualStrings(caso.esperado, resultado);
    }
}
```

## Mocks com Registro de Chamadas

Para verificar comportamento alem de retorno:

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

const LoggerMock = struct {
    chamadas: std.ArrayList([]const u8),
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator) LoggerMock {
        return .{
            .chamadas = std.ArrayList([]const u8).init(allocator),
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *LoggerMock) void {
        self.chamadas.deinit();
    }

    pub fn log(self: *LoggerMock, msg: []const u8) void {
        self.chamadas.append(msg) catch {};
    }

    // Verificacoes
    pub fn foiChamado(self: *const LoggerMock) bool {
        return self.chamadas.items.len > 0;
    }

    pub fn numeroChamadas(self: *const LoggerMock) usize {
        return self.chamadas.items.len;
    }

    pub fn ultimaChamada(self: *const LoggerMock) ?[]const u8 {
        if (self.chamadas.items.len == 0) return null;
        return self.chamadas.items[self.chamadas.items.len - 1];
    }
};

fn processarPedido(logger: *LoggerMock, pedido_id: u32) !void {
    logger.log("Processando pedido");
    // ... logica ...
    if (pedido_id == 0) return error.PedidoInvalido;
    logger.log("Pedido processado");
}

test "processarPedido loga corretamente" {
    var logger = LoggerMock.init(std.testing.allocator);
    defer logger.deinit();

    try processarPedido(&logger, 42);

    try std.testing.expect(logger.numeroChamadas() == 2);
    try std.testing.expectEqualStrings("Pedido processado", logger.ultimaChamada().?);
}
```

## Test Fixtures

Para testes que compartilham setup complexo:

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

const TestFixture = struct {
    allocator: std.mem.Allocator,
    dados: std.ArrayList(u8),
    temp_dir: ?[]const u8 = null,

    pub fn setup(allocator: std.mem.Allocator) !TestFixture {
        var fixture = TestFixture{
            .allocator = allocator,
            .dados = std.ArrayList(u8).init(allocator),
        };

        // Setup comum
        try fixture.dados.appendSlice("dados de teste");
        return fixture;
    }

    pub fn teardown(self: *TestFixture) void {
        self.dados.deinit();
        // Limpar temp files se necessario
    }
};

test "teste com fixture" {
    var fixture = try TestFixture.setup(std.testing.allocator);
    defer fixture.teardown();

    try std.testing.expect(fixture.dados.items.len > 0);
}
```

## Exercicios

1. **Mock de HTTP client:** Crie um mock que simule respostas HTTP e verifique que URLs corretas sao chamadas.

2. **Property-based testing:** Implemente testes que verifiquem propriedades (ex: sort sempre retorna array ordenado, encode/decode sao inversos).

3. **Snapshot testing:** Crie um sistema que salve a saida de funcoes em arquivos e compare com saidas anteriores.

---

## Proximo Artigo

No proximo artigo, exploramos [fuzz testing](/tutoriais/zig-testing-avancado/artigo-3-fuzz-testing/) para encontrar bugs que testes unitarios nao capturam.

### Conteudo Relacionado

- [Artigo anterior: Unit Tests Fundamentos](/tutoriais/zig-testing-avancado/artigo-1-unit-tests-fundamentos/)
- [Zig Design Patterns](/tutoriais/zig-design-patterns/) — Padroes de projeto
- [Property Testing em Zig](/tutoriais/zig-property-testing/) — Testes baseados em propriedades

---

*Duvidas sobre padroes de teste? Participe da comunidade Zig Brasil!*
