Pular para o conteudo
Voltar aos artigos
ci-cdtestesgithub-actionsvibe-codingdevops

Testes, CI/CD e GitHub Actions: o que vibe coders precisam entender

Bolinhas verdes e vermelhas no GitHub parecem simples, mas escondem um sistema que pode salvar seu projeto. Entenda como testes, CI/CD e GitHub Actions funcionam juntos.

10 min de leitura

Testes, CI/CD e GitHub Actions: o que vibe coders precisam entender

Bolinhas verdes no GitHub não significam que seu código funciona. Significam que os testes que você escreveu passaram. A diferença é enorme.


Contexto

Você está criando um app com Claude Code. Faz um push para o GitHub e, alguns minutos depois, aparece uma bolinha verde ou vermelha no repositório. Verde, você segue em frente. Vermelha, algo quebrou.

Essa dinâmica parece simples, mas esconde três conceitos distintos que trabalham juntos: testes automatizados, CI/CD e GitHub Actions. Entender cada um separadamente (e como se conectam) é a diferença entre ter uma rede de segurança real e ter apenas a ilusão de uma.

O que são testes automatizados

Um teste automatizado é um pedaço de código que verifica se outro pedaço de código funciona como esperado. Nada além disso.

Considere uma função que formata duração em segundos para o formato MM:SS:

function formatDuration(seconds: number): string {
  const mins = Math.floor(seconds / 60)
  const secs = seconds % 60
  return `${mins}:${secs.toString().padStart(2, '0')}`
}

O teste correspondente verifica cenários específicos:

import { describe, it, expect } from 'vitest'
import { formatDuration } from './utils'

describe('formatDuration', () => {
  it('deve formatar segundos como MM:SS', () => {
    expect(formatDuration(845)).toBe('14:05')
    expect(formatDuration(60)).toBe('1:00')
    expect(formatDuration(0)).toBe('0:00')
  })

  it('deve pad com zero nos segundos', () => {
    expect(formatDuration(65)).toBe('1:05')
    expect(formatDuration(3)).toBe('0:03')
  })
})

O teste chama a função com entradas conhecidas e compara o resultado com o valor esperado. Se formatDuration(845) retornar qualquer coisa diferente de "14:05", o teste falha. Simples assim.

Tipos de teste

Testes se dividem em categorias pela abrangência do que verificam:

Tipo O que testa Exemplo
Unitário Uma função ou módulo isolado formatDuration(845) retorna "14:05"
Integração Vários módulos trabalhando juntos Leitura de arquivos MDX retorna módulos ordenados com frontmatter válido
E2E (end-to-end) O app inteiro, do browser ao banco Clicar em "Comprar" redireciona para o checkout com o valor correto

A recomendação clássica é a "pirâmide de testes": muitos unitários (rápidos, baratos), alguns de integração, poucos E2E (lentos, frágeis). Na prática, o que importa é que os testes cubram o que realmente importa para o negócio.

O que testar de verdade

Aqui está a armadilha. É fácil escrever testes para coisas triviais e ignorar o que realmente importa.

Um teste que verifica se uma função de formatação funciona é útil. Mas não protege contra os problemas que causam dano real. Considere um app que processa pagamentos e serve conteúdo pago. Os testes mais valiosos seriam:

  • O webhook de pagamento processa a transação e libera o acesso corretamente?
  • O middleware de autenticação bloqueia usuários não autorizados?
  • O sistema de rotas entrega o conteúdo certo para o domínio certo?

Escrever 50 testes para funções utilitárias e zero para o fluxo de pagamento é como instalar alarme na garagem e deixar a porta da frente aberta.

O que é CI/CD

CI/CD são duas práticas complementares, normalmente implementadas juntas.

CI (Continuous Integration) é a prática de integrar código frequentemente em um repositório compartilhado, com cada integração verificada por um build automatizado que inclui testes. Martin Fowler, que popularizou o termo, define como: "uma prática de desenvolvimento onde cada membro do time integra suas mudanças no codebase junto com as mudanças dos colegas pelo menos diariamente. Cada integração é verificada por um build automatizado (incluindo testes) para detectar erros de integração o mais rápido possível."

CD (Continuous Delivery/Deployment) estende o CI levando o código validado até a produção:

  • Continuous Delivery: o código validado fica pronto para deploy, mas alguém aprova manualmente.
  • Continuous Deployment: o código validado vai automaticamente para produção sem intervenção humana.

Na prática, para a maioria dos projetos com vibe coding, o fluxo é:

push no GitHub → CI roda (lint + typecheck + testes) → merge para main → CD faz deploy automático

Organizações que adotam CI/CD fazem deploy 208 vezes mais frequentemente, com lead times 106 vezes mais rápidos, segundo o relatório DORA (DevOps Research and Assessment) do Google.

Testes versus CI/CD: qual a diferença?

Testes são o conteúdo. CI/CD é o mecanismo.

Os testes são os scripts que verificam se o código funciona. O CI/CD é o sistema que executa esses scripts automaticamente em momentos específicos (a cada push, a cada pull request, antes de cada deploy).

Sem testes, o CI pode verificar formatação e tipos, mas não sabe se o app funciona. Sem CI, os testes existem mas dependem de alguém lembrar de rodá-los. Um não substitui o outro.

Conceito O que é Sozinho
Testes Código que verifica outro código Útil, mas depende de disciplina manual
CI Automação que roda testes a cada push Mecanismo sem conteúdo se não tiver testes reais
CD Automação que faz deploy após CI passar Perigoso se CI não testar o que importa

O que é GitHub Actions

GitHub Actions é a plataforma de CI/CD integrada ao GitHub. Permite automatizar qualquer workflow de desenvolvimento diretamente no repositório.

Um workflow é definido em um arquivo YAML dentro de .github/workflows/. Cada workflow contém:

  • Trigger: o evento que dispara a execução (push, pull request, agendamento)
  • Jobs: blocos de trabalho que rodam em máquinas virtuais
  • Steps: comandos sequenciais dentro de cada job

Anatomia de um CI real

Um arquivo ci.yml típico para um projeto Next.js:

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

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'pnpm'

      - name: Install
        run: pnpm install --frozen-lockfile

      - name: Lint
        run: pnpm eslint .

      - name: Format
        run: pnpm prettier --check .

      - name: Typecheck
        run: pnpm tsc --noEmit

      - name: Test
        run: pnpm vitest run

Cada push para develop ou main dispara este workflow. Ele instala dependências, roda o linter (verifica estilo de código), o Prettier (verifica formatação), o TypeScript compiler (verifica tipos), e finalmente os testes.

Se qualquer step falhar, o workflow inteiro falha. Essa é a bolinha vermelha.

E o deploy?

O deploy normalmente vive em um workflow separado (deploy.yml) que roda apenas quando código chega na branch de produção:

name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy via SSH
        run: |
          ssh servidor "cd /app && git pull && docker compose up -d --build"

Isso é o CD em ação: assim que o CI passa e o merge para main acontece, o deploy é automático.

A armadilha das bolinhas verdes

Aqui está o ponto que vibe coders precisam entender: a bolinha verde só significa que o que você configurou para ser verificado passou. Se o CI verifica apenas lint e tipos, a bolinha verde confirma que o código está formatado e os tipos estão corretos. Nada mais.

Um cenário concreto:

  1. O CI roda: ESLint, Prettier, TypeScript, e testes unitários
  2. Tudo passa. Bolinha verde
  3. O deploy acontece automaticamente
  4. Em produção, o checkout quebra porque uma Server Action mudou a estrutura do payload

O CI fez tudo que foi configurado para fazer. O problema é que ninguém configurou um teste para o fluxo de checkout.

A rede de segurança tem buracos

Uma auditoria honesta de qualquer projeto revela a mesma coisa: os testes que existem raramente cobrem o que gera receita. É mais rápido escrever um teste para uma função de formatação do que simular um webhook de pagamento. Os testes triviais se acumulam e os testes críticos ficam para "depois".

O resultado é uma infraestrutura de CI/CD montada corretamente, mas que valida o conteúdo errado. A maquinaria funciona. O conteúdo da esteira é que está incompleto.

Checklist de autoavaliação:

  • O fluxo que gera receita (pagamento, checkout) tem pelo menos um teste?
  • O sistema de autenticação tem testes?
  • O CI roda build além de typecheck? (typecheck não pega todos os erros de build)
  • Existe pelo menos um teste E2E para o caminho crítico?
  • O coverage mínimo está definido e é enforced no CI?

Se a resposta para a maioria for "não", a bolinha verde está dando uma falsa sensação de segurança.

Na Prática

Passo a passo para configurar testes e CI em um projeto Next.js com Vitest:

1. Instale Vitest e dependências de teste

pnpm add -D vitest @vitejs/plugin-react @testing-library/react

2. Configure o vitest.config.ts

import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import path from 'path'

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'node',
    coverage: {
      provider: 'v8',
      thresholds: { lines: 60 }
    }
  },
  resolve: {
    alias: { '@': path.resolve(__dirname, './src') }
  }
})

3. Escreva seu primeiro teste

Crie um arquivo src/lib/__tests__/exemplo.test.ts:

import { describe, it, expect } from 'vitest'

describe('minha função', () => {
  it('deve retornar o resultado esperado', () => {
    const resultado = minhaFuncao('entrada')
    expect(resultado).toBe('saída esperada')
  })
})

4. Adicione scripts ao package.json

{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage"
  }
}

5. Crie o workflow de CI

Crie .github/workflows/ci.yml com o conteúdo da seção "Anatomia de um CI real" acima.

6. Faça push e observe

git add . && git commit -m "feat: add CI with tests" && git push

Abra a aba Actions no GitHub e acompanhe a execução.

Dica: Comece testando o caminho mais crítico do seu app (o que gera receita ou o que quebraria a experiência do usuário). Um único teste no fluxo de pagamento vale mais que 20 testes em funções utilitárias.

O que isso significa para vibe coding

Vibe coding com AI assistants torna trivial gerar código rapidamente. Isso é um superpoder e um risco ao mesmo tempo. Quanto mais rápido você gera código, mais rápido pode introduzir bugs que só aparecem em produção.

Testes e CI/CD não são burocracia de empresa grande. São o mecanismo que permite mover rápido sem quebrar o que já funciona. A bolinha verde no GitHub não é decoração. É uma evidência concreta de que verificações específicas passaram.

Mas evidência só tem valor se as verificações forem relevantes. Configurar CI sem testes nos caminhos críticos é como ter um alarme de incêndio que só detecta fumaça na cozinha. Melhor que nada. Mas não substitui detectores no resto da casa.

A próxima vez que vir uma bolinha verde, pergunte: "o que exatamente isso está verificando?" A resposta pode ser mais reveladora do que a cor.


Referências