Testes de Integração Rust com Docker: Guia Completo

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Testes de Integração Rust com Docker: do A ao Z

\n\n

Testes de integração são uma etapa criticamente importante no desenvolvimento de aplicações confiáveis em Rust. Ao contrário dos testes unitários, os testes de integração verificam a interação dos componentes com dependências externas: bancos de dados, caches, filas de mensagens. O Docker é ideal para criar ambientes de teste isolados e reproduzíveis. Neste artigo, vamos abordar como configurar testes de integração de um projeto Rust usando Docker, começando pela arquitetura básica e indo até CI/CD.

\n\n

1. Por que o Docker é a melhor escolha para testes de integração Rust

\n\n

Rust compila para código nativo, mas suas aplicações geralmente dependem de serviços externos. O Docker resolve três problemas principais:

\n
    \n
  • Isolamento: cada teste é executado em um contêiner limpo, eliminando a influência de execuções anteriores.
  • \n
  • Reprodutibilidade: versões exatas do PostgreSQL, Redis ou Kafka garantem o mesmo comportamento na máquina local e no CI.
  • \n
  • Velocidade: as imagens Docker são armazenadas em cache e os testes Rust são compilados uma vez.
  • \n
\n\n

Vamos considerar um projeto típico: um servidor web em Actix-web, trabalhando com PostgreSQL. Nosso objetivo é escrever um teste que inicie um contêiner com o banco de dados, execute migrações e verifique operações CRUD.

\n\n

2. Configuração da infraestrutura de teste com Docker Compose

\n\n

Para maior comodidade, usamos docker-compose.yml, que descreve todas as dependências. Vamos criar o arquivo na raiz do projeto:

\n\n
version: '3.8'\nservices:\n  postgres_test:\n    image: postgres:15-alpine\n    environment:\n      POSTGRES_USER: test_user\n      POSTGRES_PASSWORD: test_pass\n      POSTGRES_DB: test_db\n    ports:\n      - "5433:5432"\n    tmpfs: /var/lib/postgresql/data  # dados em memória para velocidade
\n\n

Observe o tmpfs — isso acelera os testes, pois os dados não são gravados em disco. A porta 5433 foi escolhida para não conflitar com o banco de dados local.

\n\n

Agora, vamos escrever uma função Rust para gerenciar os contêineres. Usamos a crate testcontainers — ela permite iniciar contêineres Docker programaticamente:

\n\n
// tests/common/mod.rs\nuse testcontainers::{clients, images::postgres::Postgres, Container};\n\npub struct TestDb {\n    pub container: Container<'static, Postgres>,\n    pub connection_string: String,\n}\n\nimpl TestDb {\n    pub fn new() -> Self {\n        let docker = clients::Cli::default();\n        let postgres_image = Postgres::default();\n        let container = docker.run(postgres_image);\n        \n        let host = container.get_host();\n        let port = container.get_host_port_ipv4(5432);\n        let connection_string = format!(\n            "postgres://postgres:postgres@{}:{}/postgres",\n            host, port\n        );\n        \n        TestDb { container, connection_string }\n    }\n}
\n\n

Este módulo pode ser reutilizado em todos os testes de integração. O contêiner será removido automaticamente se os testes falharem ou quando o processo terminar.

\n\n

3. Escrevendo testes de integração com requisições reais

\n\n

Vamos criar um teste que verifica a inserção e leitura de um usuário. Suponha que temos uma estrutura User e funções create_user e get_user no código principal.

\n\n
// tests/integration_test.rs\nmod common;\n\nuse sqlx::PgPool;\nuse your_crate::{create_user, get_user, User};\n\n#[tokio::test]\nasync fn test_create_and_get_user() {\n    let test_db = common::TestDb::new();\n    let pool = PgPool::connect(&test_db.connection_string)\n        .await\n        .expect("Failed to connect to DB");\n    \n    // Executa as migrações (se estiver usando sqlx)\n    sqlx::migrate!().run(&pool).await.unwrap();\n    \n    // Cria um usuário\n    let user = User {\n        name: "Alice".into(),\n        email: "alice@example.com".into(),\n    };\n    let created = create_user(&pool, &us                

Blogs

Book Recommendations