Destino Dev

Blog

Testes em CI com GitHub Actions e Docker Compose para Postgres e Redis

Por John Pires · 19 de março de 2026

Testes em CI com GitHub Actions e Docker Compose para Postgres e Redis

Por que aprender isso em 2026

Contexto: o problema que resolvemos

Projetos modernos usam serviços externos: bancos, caches, filas. Testar código que depende desses serviços no seu computador é fácil — você instala, configura e testa. O problema é reproduzir esse ambiente no CI. Testes que passam localmente podem falhar no pipeline por causa de versões, configurações ou serviços não disponíveis. Em 2026, com arquiteturas distribuídas e infra como código, saber montar um ambiente de testes no CI que reproduza Postgres e Redis é habilidade essencial.

Alternativas e por que escolher Docker Compose + GitHub Actions

Existem várias alternativas: usar serviços gerenciados, usar mocks, usar imagens de serviços do próprio runner (GitHub Actions services) ou rodar containers com Docker Compose. Cada alternativa tem vantagens: mocks são rápidos, serviços gerenciados representam produção, e services nativos do Actions são simples. Docker Compose oferece equilíbrio: você descreve o ambiente em um arquivo declarativo, pode versionar e rodar localmente e no CI, garantindo consistência.

Para iniciantes, Docker Compose reduz a curva de aprendizado porque usa um arquivo YAML único para orquestrar Postgres e Redis. Combinado com GitHub Actions, você automatiza testes em cada commit. Este artigo mostra um caminho prático do zero.

Configuração do ambiente

O que precisaremos instalar localmente

Antes de avançar, é importante ter ferramentas mínimas no seu computador: Docker (inclui engine e compose), Git, e Python 3.11 (ou outra versão que você prefira). No CI você não precisa instalar tudo — o GitHub Actions já fornece runners com Docker instalado. Localmente você testará o mesmo arquivo docker-compose para validar comportamento.

Estrutura do repositório

Crie uma estrutura simples para acompanhar os exemplos. Abaixo um layout sugerido:

  • app/ (código python que acessa Postgres e Redis)
  • tests/ (testes pytest)
  • docker-compose.yml (define Postgres e Redis)
  • .github/workflows/ci.yml (workflow do GitHub Actions)
  • requirements.txt

Essa organização mantém infra e código juntos, facilitando reproduzir o ambiente no CI e localmente.

Hello World progressivo

Primeiro: docker-compose.yml minimal para Postgres e Redis

Este arquivo cria dois serviços: postgres e redis. Ele define variáveis de ambiente essenciais e publica portas para que a aplicação teste os serviços via localhost.

version: '3.8'
services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_USER: test_user
      POSTGRES_PASSWORD: test_pass
      POSTGRES_DB: test_db
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U test_user"]
      interval: 5s
      retries: 5

  redis:
    image: redis:7
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      retries: 5

O que faz: cria containers Postgres e Redis com healthchecks. Depois: você poderá iniciar com docker compose up -d e acessar Postgres em localhost:5432 e Redis em localhost:6379.

Segundo: um cliente Python simples para testar conexões

Crie um pequeno módulo que abre conexões ao Postgres e ao Redis. Assim os testes serão reais, conectando aos serviços.

# app/db.py
import os
import psycopg2
import redis

PG_USER = os.getenv('POSTGRES_USER', 'test_user')
PG_PASS = os.getenv('POSTGRES_PASSWORD', 'test_pass')
PG_DB = os.getenv('POSTGRES_DB', 'test_db')
PG_HOST = os.getenv('PGHOST', 'localhost')
PG_PORT = int(os.getenv('PGPORT', 5432))

REDIS_HOST = os.getenv('REDIS_HOST', 'localhost')
REDIS_PORT = int(os.getenv('REDIS_PORT', 6379))


def get_pg_conn():
    conn = psycopg2.connect(
        dbname=PG_DB, user=PG_USER, password=PG_PASS, host=PG_HOST, port=PG_PORT
    )
    return conn


def get_redis_conn():
    return redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True)

O que faz: define funções que retornam conexões para Postgres e Redis. Depois: nos testes você importa essas funções para validar operações básicas.

Mini-projeto prático

Testes com pytest: integração básica

Crie um teste que verifica se você consegue escrever e ler um valor no Redis e criar uma tabela simples no Postgres. Esses testes validam que os serviços estão prontos e que o código de integração funciona.

# tests/test_integration.py
import time
import pytest
from app.db import get_pg_conn, get_redis_conn


def wait_for_postgres(retries=10, delay=1):
    for _ in range(retries):
        try:
            conn = get_pg_conn()
            conn.close()
            return True
        except Exception:
            time.sleep(delay)
    return False


def wait_for_redis(retries=10, delay=0.5):
    r = get_redis_conn()
    for _ in range(retries):
        try:
            if r.ping():
                return True
        except Exception:
            time.sleep(delay)
    return False


def test_redis_set_get():
    assert wait_for_redis(), 'Redis não respondeu a tempo'
    r = get_redis_conn()
    r.set('ci:test:key', 'value123')
    assert r.get('ci:test:key') == 'value123'


def test_postgres_create_table():
    assert wait_for_postgres(), 'Postgres não respondeu a tempo'
    conn = get_pg_conn()
    cur = conn.cursor()
    cur.execute('CREATE TABLE IF NOT EXISTS ci_test (id serial primary key, name text);')
    cur.execute("INSERT INTO ci_test (name) VALUES ('alice') RETURNING id;")
    inserted = cur.fetchone()[0]
    conn.commit()
    cur.execute('SELECT name FROM ci_test WHERE id=%s', (inserted,))
    assert cur.fetchone()[0] == 'alice'
    cur.close()
    conn.close()

O que faz: garante que os serviços aceitam conexões, escreve e lê dados básicos. Depois: quando executado no CI, estes testes validarão a integração real com Postgres e Redis.

Workflow GitHub Actions usando docker compose

Agora vamos ao arquivo de CI. Esse workflow roda em ubuntu-latest, sobe os containers com docker compose, aguarda healthchecks e executa os testes com pytest.

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install docker compose (if needed)
        run: |
          python -m pip install --upgrade pip
          pip install docker-compose

      - name: Start services
        run: |
          docker compose up -d --build

      - name: Wait for services
        run: |
          # espera simples -- os testes também esperam
          for i in {1..20}; do
            docker compose ps
            sleep 1
          done

      - name: Install dependencies
        run: |
          pip install -r requirements.txt

      - name: Run tests
        env:
          PGHOST: localhost
          PGPORT: '5432'
          POSTGRES_USER: test_user
          POSTGRES_PASSWORD: test_pass
          POSTGRES_DB: test_db
          REDIS_HOST: localhost
          REDIS_PORT: '6379'
        run: |
          pytest -q

      - name: Tear down
        if: always()
        run: docker compose down -v

O que faz: sobe os serviços, instala dependências Python e roda pytest apontando para localhost. Depois: garante que os containers são derrubados mesmo se os testes falharem.

Erros comuns, armadilhas e dicas práticas

Problemas de timing e healthchecks

Um erro recorrente é considerar o container 'up' sinônimo de service pronto. Containers iniciam rapidamente, mas Postgres pode levar segundos para aceitar conexões. Healthchecks ajudam, mas adicionar loops de espera nos testes (como os exemplos de wait_for_postgres/redis) é prática robusta. No CI aumente timeout se necessário.

Portas, variáveis de ambiente e isolamento

Mapear portas (5432, 6379) para o host facilita testes locais. No entanto, se você rodar múltiplos pipelines simultâneos usando o mesmo runner e portas fixas, haverá conflito. Em GitHub Actions os runners são isolados por job, então normalmente não há conflito. Outro problema é esquecer de exportar variáveis de ambiente no job — sempre defina PGHOST/POSTGRES_* e REDIS_* no ambiente do step de teste.

Tip: prefira usar variáveis de ambiente explícitas no workflow e nos testes. Isso evita surpresas quando mudar versões de imagens ou nomes de serviço.

Erros de permissões e volumes

Ao usar volumes com Postgres, permissões de filesystem podem impedir a inicialização do banco. Para testes em CI, prefira não montar volumes persistentes, ou use volumes temporários gerenciados pelo Docker Compose (evite mapear diretórios locais no CI).

Dicas rápidas

  • Use imagens oficiais (postgres:15, redis:7) para consistência.
  • Evite dados persistentes no CI: comece com um banco limpo para cada job.
  • Faça testes idempotentes: crie e limpe tabelas dentro do teste.
  • Registre logs com docker compose logs para depurar falhas no CI.

Recursos para continuar estudando

Documentação essencial

Leia a documentação oficial do Docker Compose e do GitHub Actions. Elas explicam nuances de network, volumes e como runners se comportam. Para Postgres e Redis, a documentação oficial também traz parâmetros importantes de configuração que afetam testes (como arquivos de configuração, autenticacao e persistência).

Práticas e leituras recomendadas

Estude esses tópicos para melhorar sua pipeline:

  1. Testes de integração vs testes unitários (quando mockar e quando não mockar)
  2. Como versionar imagens e dependências (guardar versões no docker-compose)
  3. Como rodar migrations automaticamente no CI antes dos testes
  4. Como usar secrets no Actions para dados sensíveis

Um bom próximo passo é acrescentar um job para rodar migrations com Alembic (para Python) antes dos testes e coletar coverage para analisar gaps.

Conclusão prática

Você já tem um fluxo: descrito em Docker Compose, testado localmente e automatizado no GitHub Actions. Essa solução entrega consistência entre ambientes, reduz falsos positivos e facilita depuração. Em 2026, pipelines continuam centrais para entrega confiável; aprender a orquestrar serviços no CI é parte fundamental da caixa de ferramentas do desenvolvedor.

Resumo das ações concretas que você pode executar agora:

  • Crie o repositório com os arquivos mostrados.
  • Rode docker compose up -d localmente e execute os testes com pytest.
  • Adicione o workflow CI e observe os testes rodando em cada PR.

Este artigo foi prático: apresentou o problema, avaliou alternativas e levou você a uma solução funcional com exemplos reais. Para avançar, inclua migrations, monitoramento e cobertura de testes no pipeline.

Boa prática: mantenha os arquivos de orquestração (docker-compose, workflows) junto ao código. Eles documentam como o sistema precisa estar para funcionar — isso é infra como código em ação.

Assuntos: Docker, github-actions, PostgreSQL, NoSQL, ci, DevOps

Leia também