---
title: "Testes de Integracao em Zig: I/O, Networking e End-to-End"
url: "https://ziglang.com.br/tutoriais/zig-integration-tests/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-integration-tests.MD"
description: "Domine testes de integracao em Zig. Testando I/O, file system, networking, subprocessos e testes end-to-end. Tutorial completo em portugues."
date: "2026-02-21"
author: ""
---

# Testes de Integracao em Zig: I/O, Networking e End-to-End

Domine testes de integracao em Zig. Testando I/O, file system, networking, subprocessos e testes end-to-end. Tutorial completo em portugues.


Testes unitarios validam funcoes isoladas. Testes de integracao validam que componentes funcionam juntos corretamente — com o file system, rede, banco de dados e subprocessos reais. Zig oferece ferramentas poderosas para escrever testes de integracao confiaveis sem frameworks externos.

> Continuacao do artigo sobre [fuzz testing em Zig](/tutoriais/zig-testing-avancado/artigo-3-fuzz-testing/).

## Unit Tests vs Integration Tests

| Aspecto | Unit Tests | Integration Tests |
|---------|-----------|-------------------|
| Escopo | Funcao/modulo individual | Multiplos componentes |
| Dependencias | Mockadas | Reais |
| Velocidade | Milissegundos | Segundos |
| Isolamento | Total | Parcial |
| Foco | Logica de negocio | Interacao entre partes |

## Testando File System

### Diretorio Temporario para Testes

O Zig fornece `std.testing.tmpDir()` para criar diretorios temporarios que sao limpos automaticamente:

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

/// Sistema de configuracao que le/escreve arquivos
const ConfigManager = struct {
    dir: fs.Dir,

    const Config = struct {
        porta: u16,
        host: []const u8,
        max_conexoes: u32,
    };

    pub fn salvar(self: ConfigManager, config: Config) !void {
        var file = try self.dir.createFile("config.ini", .{});
        defer file.close();

        var writer = file.writer();
        try writer.print("[server]\n", .{});
        try writer.print("porta={d}\n", .{config.porta});
        try writer.print("host={s}\n", .{config.host});
        try writer.print("max_conexoes={d}\n", .{config.max_conexoes});
    }

    pub fn carregar(self: ConfigManager, allocator: std.mem.Allocator) !Config {
        var file = try self.dir.openFile("config.ini", .{});
        defer file.close();

        const conteudo = try file.readToEndAlloc(allocator, 4096);
        defer allocator.free(conteudo);

        // Parse simplificado
        var config = Config{
            .porta = 8080,
            .host = "localhost",
            .max_conexoes = 100,
        };

        var linhas = std.mem.splitScalar(u8, conteudo, '\n');
        while (linhas.next()) |linha| {
            if (std.mem.startsWith(u8, linha, "porta=")) {
                config.porta = try std.fmt.parseInt(u16, linha["porta=".len..], 10);
            } else if (std.mem.startsWith(u8, linha, "max_conexoes=")) {
                config.max_conexoes = try std.fmt.parseInt(u32, linha["max_conexoes=".len..], 10);
            }
        }

        return config;
    }
};

test "integracao: salvar e carregar configuracao" {
    // Arrange: diretorio temporario limpo automaticamente
    var tmp = std.testing.tmpDir(.{});
    defer tmp.cleanup();

    const manager = ConfigManager{ .dir = tmp.dir };

    // Act: salvar configuracao
    try manager.salvar(.{
        .porta = 3000,
        .host = "0.0.0.0",
        .max_conexoes = 500,
    });

    // Assert: carregar e verificar
    const config = try manager.carregar(std.testing.allocator);
    try std.testing.expectEqual(@as(u16, 3000), config.porta);
    try std.testing.expectEqual(@as(u32, 500), config.max_conexoes);
}

test "integracao: config inexistente retorna erro" {
    var tmp = std.testing.tmpDir(.{});
    defer tmp.cleanup();

    const manager = ConfigManager{ .dir = tmp.dir };

    // Tentar carregar config que nao existe
    const resultado = manager.carregar(std.testing.allocator);
    try std.testing.expectError(error.FileNotFound, resultado);
}
```

### Testando Operacoes de Arquivo

```zig
const LogWriter = struct {
    arquivo: fs.File,
    bytes_escritos: u64 = 0,

    pub fn init(dir: fs.Dir) !LogWriter {
        const arquivo = try dir.createFile("app.log", .{ .truncate = false });
        return .{ .arquivo = arquivo };
    }

    pub fn escrever(self: *LogWriter, nivel: []const u8, mensagem: []const u8) !void {
        var writer = self.arquivo.writer();
        const timestamp = std.time.timestamp();
        const n = try writer.print("[{d}] [{s}] {s}\n", .{ timestamp, nivel, mensagem });
        self.bytes_escritos += n;
    }

    pub fn fechar(self: *LogWriter) void {
        self.arquivo.close();
    }
};

test "integracao: log writer escreve corretamente" {
    var tmp = std.testing.tmpDir(.{});
    defer tmp.cleanup();

    // Escrever logs
    {
        var logger = try LogWriter.init(tmp.dir);
        defer logger.fechar();

        try logger.escrever("INFO", "Servidor iniciado");
        try logger.escrever("WARN", "Memoria alta");
        try logger.escrever("ERROR", "Conexao perdida");

        try expect(logger.bytes_escritos > 0);
    }

    // Verificar conteudo do arquivo
    const conteudo = try tmp.dir.readFileAlloc(std.testing.allocator, "app.log", 4096);
    defer std.testing.allocator.free(conteudo);

    try expect(std.mem.indexOf(u8, conteudo, "Servidor iniciado") != null);
    try expect(std.mem.indexOf(u8, conteudo, "Memoria alta") != null);
    try expect(std.mem.indexOf(u8, conteudo, "Conexao perdida") != null);

    // Verificar que ha 3 linhas
    var count: usize = 0;
    for (conteudo) |c| {
        if (c == '\n') count += 1;
    }
    try std.testing.expectEqual(@as(usize, 3), count);
}
```

## Testando Networking

### Servidor TCP de Teste

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

/// Servidor echo simples para testes
const EchoServer = struct {
    server: net.Server,
    thread: ?std.Thread = null,

    pub fn iniciar(porta: u16) !EchoServer {
        const endereco = net.Address.initIp4(.{ 127, 0, 0, 1 }, porta);
        var server = try endereco.listen(.{
            .reuse_address = true,
        });

        return .{ .server = server };
    }

    pub fn rodarEmBackground(self: *EchoServer) !void {
        self.thread = try std.Thread.spawn(.{}, aceitarConexoes, .{&self.server});
    }

    fn aceitarConexoes(server: *net.Server) void {
        while (true) {
            const conn = server.accept() catch break;
            defer conn.stream.close();

            var buf: [1024]u8 = undefined;
            while (true) {
                const n = conn.stream.read(&buf) catch break;
                if (n == 0) break;
                conn.stream.writeAll(buf[0..n]) catch break;
            }
        }
    }

    pub fn parar(self: *EchoServer) void {
        self.server.deinit();
        if (self.thread) |t| t.join();
    }

    pub fn porta(self: *EchoServer) u16 {
        return self.server.listen_address.getPort();
    }
};

test "integracao: echo server TCP" {
    // Arrange: iniciar servidor na porta 0 (kernel escolhe porta livre)
    var server = try EchoServer.iniciar(0);
    defer server.parar();
    try server.rodarEmBackground();

    const porta_server = server.porta();

    // Act: conectar como cliente
    const endereco = net.Address.initIp4(.{ 127, 0, 0, 1 }, porta_server);
    const stream = try net.tcpConnectToAddress(endereco);
    defer stream.close();

    // Enviar mensagem
    try stream.writeAll("Ola Zig!");

    // Receber resposta
    var buf: [1024]u8 = undefined;
    const n = try stream.read(&buf);

    // Assert: servidor deve ecoar a mensagem
    try std.testing.expectEqualStrings("Ola Zig!", buf[0..n]);
}
```

### Testando HTTP Client

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

/// Cliente HTTP simples
const HttpClient = struct {
    allocator: std.mem.Allocator,

    const Resposta = struct {
        status: u16,
        body: []const u8,
        allocator: std.mem.Allocator,

        pub fn deinit(self: *Resposta) void {
            self.allocator.free(self.body);
        }
    };

    pub fn get(self: HttpClient, url: []const u8) !Resposta {
        const uri = try std.Uri.parse(url);

        var client = std.http.Client{ .allocator = self.allocator };
        defer client.deinit();

        var buf: [4096]u8 = undefined;
        var req = try client.open(.GET, uri, .{
            .server_header_buffer = &buf,
        });
        defer req.deinit();

        try req.send();
        try req.wait();

        const body = try req.reader().readAllAlloc(self.allocator, 1024 * 1024);

        return .{
            .status = @intFromEnum(req.response.status),
            .body = body,
            .allocator = self.allocator,
        };
    }
};

test "integracao: HTTP GET" {
    // Este teste requer acesso a rede — pode ser marcado como skip
    // em ambientes sem rede
    const client = HttpClient{ .allocator = std.testing.allocator };

    var resposta = client.get("http://httpbin.org/get") catch |err| {
        // Se nao houver rede disponivel, pular o teste
        if (err == error.ConnectionRefused or err == error.NetworkUnreachable) {
            return;
        }
        return err;
    };
    defer resposta.deinit();

    try std.testing.expectEqual(@as(u16, 200), resposta.status);
    try std.testing.expect(resposta.body.len > 0);
}
```

## Testando Subprocessos

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

test "integracao: executar subprocesso" {
    const allocator = std.testing.allocator;

    // Executar um comando e capturar saida
    var child = std.process.Child.init(
        &.{ "echo", "Hello from Zig" },
        allocator,
    );
    child.stdout_behavior = .Pipe;

    try child.spawn();

    const stdout = try child.stdout.?.reader().readAllAlloc(allocator, 4096);
    defer allocator.free(stdout);

    const resultado = try child.wait();

    try std.testing.expectEqual(std.process.Child.Term{ .Exited = 0 }, resultado);
    try std.testing.expectEqualStrings("Hello from Zig\n", stdout);
}

test "integracao: subprocesso com erro" {
    const allocator = std.testing.allocator;

    var child = std.process.Child.init(
        &.{ "ls", "/diretorio/inexistente" },
        allocator,
    );
    child.stderr_behavior = .Pipe;

    try child.spawn();

    const stderr = try child.stderr.?.reader().readAllAlloc(allocator, 4096);
    defer allocator.free(stderr);

    const resultado = try child.wait();

    // ls retorna codigo de saida != 0 para diretorio inexistente
    switch (resultado) {
        .Exited => |code| try std.testing.expect(code != 0),
        else => try std.testing.expect(false),
    }
    try std.testing.expect(stderr.len > 0);
}
```

## Testes End-to-End

### Testando uma Aplicacao Completa

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

/// Aplicacao de key-value store com persistencia
const KVStore = struct {
    dados: std.StringHashMap([]const u8),
    dir: std.fs.Dir,
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator, dir: std.fs.Dir) KVStore {
        return .{
            .dados = std.StringHashMap([]const u8).init(allocator),
            .dir = dir,
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *KVStore) void {
        var it = self.dados.iterator();
        while (it.next()) |entry| {
            self.allocator.free(entry.key_ptr.*);
            self.allocator.free(entry.value_ptr.*);
        }
        self.dados.deinit();
    }

    pub fn set(self: *KVStore, chave: []const u8, valor: []const u8) !void {
        const k = try self.allocator.dupe(u8, chave);
        errdefer self.allocator.free(k);
        const v = try self.allocator.dupe(u8, valor);
        errdefer self.allocator.free(v);

        if (self.dados.fetchPut(k, v)) |old| {
            self.allocator.free(old.key);
            self.allocator.free(old.value);
        }
    }

    pub fn get(self: *KVStore, chave: []const u8) ?[]const u8 {
        return self.dados.get(chave);
    }

    pub fn persistir(self: *KVStore) !void {
        var file = try self.dir.createFile("store.dat", .{});
        defer file.close();

        var writer = file.writer();
        var it = self.dados.iterator();
        while (it.next()) |entry| {
            const key_len: u32 = @intCast(entry.key_ptr.*.len);
            const val_len: u32 = @intCast(entry.value_ptr.*.len);
            try writer.writeInt(u32, key_len, .little);
            try writer.writeAll(entry.key_ptr.*);
            try writer.writeInt(u32, val_len, .little);
            try writer.writeAll(entry.value_ptr.*);
        }
    }

    pub fn restaurar(self: *KVStore) !void {
        var file = self.dir.openFile("store.dat", .{}) catch |err| {
            if (err == error.FileNotFound) return;
            return err;
        };
        defer file.close();

        var reader = file.reader();
        while (true) {
            const key_len = reader.readInt(u32, .little) catch break;
            const key = try self.allocator.alloc(u8, key_len);
            errdefer self.allocator.free(key);
            try reader.readNoEof(key);

            const val_len = try reader.readInt(u32, .little);
            const val = try self.allocator.alloc(u8, val_len);
            errdefer self.allocator.free(val);
            try reader.readNoEof(val);

            try self.dados.put(key, val);
        }
    }
};

test "e2e: KV store com persistencia" {
    const allocator = std.testing.allocator;
    var tmp = std.testing.tmpDir(.{});
    defer tmp.cleanup();

    // Fase 1: Criar e popular a store
    {
        var store = KVStore.init(allocator, tmp.dir);
        defer store.deinit();

        try store.set("nome", "Zig Brasil");
        try store.set("versao", "0.14.0");
        try store.set("linguagem", "Zig");

        try std.testing.expectEqualStrings("Zig Brasil", store.get("nome").?);

        // Persistir no disco
        try store.persistir();
    }

    // Fase 2: Restaurar de uma nova instancia
    {
        var store = KVStore.init(allocator, tmp.dir);
        defer store.deinit();

        try store.restaurar();

        // Verificar que todos os dados foram restaurados
        try std.testing.expectEqualStrings("Zig Brasil", store.get("nome").?);
        try std.testing.expectEqualStrings("0.14.0", store.get("versao").?);
        try std.testing.expectEqualStrings("Zig", store.get("linguagem").?);

        // Chave inexistente retorna null
        try std.testing.expect(store.get("inexistente") == null);
    }
}
```

## Helpers para Testes de Integracao

### Retry com Timeout

```zig
/// Repetir uma operacao ate sucesso ou timeout
fn retryAteTimeout(
    comptime T: type,
    func: fn () anyerror!T,
    timeout_ms: u64,
) !T {
    const inicio = std.time.milliTimestamp();

    while (true) {
        if (func()) |resultado| {
            return resultado;
        } else |_| {
            const elapsed: u64 = @intCast(std.time.milliTimestamp() - @as(i64, @intCast(inicio)));
            if (elapsed > timeout_ms) {
                return error.Timeout;
            }
            std.time.sleep(100 * std.time.ns_per_ms);
        }
    }
}

test "integracao: retry com timeout" {
    var contador: u32 = 0;

    const resultado = try retryAteTimeout(u32, struct {
        fn call() anyerror!u32 {
            // Simular operacao que falha nas primeiras tentativas
            return 42;
        }
    }.call, 5000);

    _ = contador;
    try std.testing.expectEqual(@as(u32, 42), resultado);
}
```

## Conclusao

Testes de integracao sao essenciais para garantir que seu software funciona no mundo real — com discos, redes e processos reais. Zig facilita a escrita desses testes com ferramentas built-in como `std.testing.tmpDir()`, abstrações de rede e suporte a subprocessos. A chave e manter os testes reproduziveis, isolados e rapidos o suficiente para rodar frequentemente.

## Proximo Artigo

No [Artigo 5: CI/CD e Automacao](/tutoriais/zig-testing-avancado/artigo-5-ci-cd-testing/), vamos automatizar a execucao de todos esses testes com GitHub Actions e pipelines de CI/CD.

## Conteudo Relacionado

- [Fuzz Testing em Zig](/tutoriais/zig-testing-avancado/artigo-3-fuzz-testing/) — Artigo anterior
- [CI/CD e Automacao](/tutoriais/zig-testing-avancado/artigo-5-ci-cd-testing/) — Proximo artigo
- [File I/O em Zig](/tutoriais/zig-file-io/) — Tutorial de I/O
- [Networking em Zig](/tutoriais/zig-networking-sockets/) — Tutorial de networking
- [API REST Completa com Zig](/artigos/zig-api-rest-completa/) — Projeto REST
