Nesta aula, vamos explorar como integrar diferentes tipos de testes no pipeline de CI/CD, garantindo que cada commit seja validado de forma automática e eficiente. Testes são a base da confiança no processo de entrega contínua, e saber quando e como executá-los é fundamental para um pipeline robusto.

Veremos os três grandes grupos de testes — unitários, de integração e end-to-end (E2E) —, discutiremos a ordem ideal de execução, como usar a qualidade como gate para impedir que código problemático avance e como a cobertura de código pode ser uma métrica útil, mas não absoluta.

Testes unitários, integração, E2E

Os testes unitários verificam o menor componente isolado do sistema, como uma função ou método. São rápidos, fáceis de manter e devem ser executados a cada commit. Exemplo em Bash para rodar testes unitários com pytest (Python):

# Executar testes unitários com pytest e gerar relatório de cobertura
pytest tests/unit --cov=src --cov-report=term-missing --junitxml=reports/unit.xml

Os testes de integração validam a interação entre componentes, como banco de dados, APIs ou filas. São mais lentos e consomem mais recursos. Exemplo com Docker Compose para subir dependências e rodar testes:

# Subir serviços necessários e executar testes de integração
docker-compose -f docker-compose.test.yml up -d
pytest tests/integration --junitxml=reports/integration.xml
docker-compose -f docker-compose.test.yml down

Os testes E2E simulam o usuário final percorrendo fluxos completos. São os mais lentos e frágeis, mas essenciais para validar o sistema como um todo. Exemplo com Cypress:

# Rodar testes E2E com Cypress em modo headless
npx cypress run --headless --browser chrome --reporter junit --reporter-options mochaFile=reports/e2e.xml

Quando rodar cada um

A ordem de execução no pipeline deve refletir o custo e a confiabilidade de cada tipo. Testes unitários são rápidos e devem rodar primeiro, para dar feedback imediato. Se falharem, o pipeline para (fail fast). Exemplo de pipeline no GitLab CI:

stages:
  - unit
  - integration
  - e2e

unit-tests:
  stage: unit
  script:
    - pytest tests/unit
  only:
    - merge_requests

integration-tests:
  stage: integration
  script:
    - docker-compose up -d
    - pytest tests/integration
    - docker-compose down
  only:
    - main

e2e-tests:
  stage: e2e
  script:
    - npx cypress run
  only:
    - main

Testes de integração e E2E, por serem mais lentos, podem ser executados apenas em branches principais ou antes do deploy. A ideia é balancear feedback rápido com cobertura adequada.

Qualidade como gate

Qualidade como gate significa que o pipeline impede que código de baixa qualidade avance para ambientes superiores. Métricas como cobertura mínima, número de falhas ou tempo de execução podem ser usadas. Exemplo com SonarQube:

# Após rodar testes, analisar com SonarQube e falhar se qualidade não atingir o gate
sonar-scanner -Dsonar.projectKey=meu-projeto -Dsonar.host.url=http://sonarqube:9000 -Dsonar.login=token
# Verificar status do quality gate
curl -u token: "http://sonarqube:9000/api/qualitygates/project_status?projectKey=meu-projeto" | jq -r '.projectStatus.status'
if [ "$status" != "OK" ]; then exit 1; fi

Outra abordagem é usar thresholds no próprio pipeline, como exigir cobertura mínima de 80% nos testes unitários. Exemplo com pytest-cov:

# Falhar se cobertura for menor que 80%
pytest tests/unit --cov=src --cov-fail-under=80

Isso garante que o código só avance se atender aos critérios definidos pela equipe.

Cobertura

Cobertura de código mede a porcentagem de linhas, branches ou funções executadas pelos testes. Ferramentas como pytest-cov, JaCoCo (Java) ou Istanbul (JavaScript) geram relatórios. Exemplo de configuração no pytest:

# pytest.ini
[pytest]
addopts = --cov=src --cov-report=html:reports/coverage --cov-report=xml:reports/coverage.xml

No pipeline, a cobertura pode ser publicada como artefato ou enviada para um serviço como Codecov. Exemplo no GitHub Actions:

- name: Upload coverage to Codecov
  uses: codecov/codecov-action@v3
  with:
    files: reports/coverage.xml
    flags: unittests

É importante lembrar que cobertura alta não garante qualidade: testes podem ser mal escritos ou não cobrir cenários importantes. Use a cobertura como guia, não como meta única.

Boas práticas e observações finais

Mantenha os testes unitários rápidos (segundos), os de integração em minutos e os E2E em no máximo 10-15 minutos. Use relatórios centralizados (JUnit XML) para facilitar a análise de falhas. Considere executar testes em paralelo para reduzir o tempo total do pipeline. E lembre-se: o pipeline é um aliado, não um obstáculo; ajuste os gates conforme a maturidade do time.

Referências

Exercícios

  1. Configure um pipeline no GitLab CI que execute testes unitários e de integração, parando se os unitários falharem.
  2. ✓ Resposta:Exemplo de .gitlab-ci.yml:
    stages:
      - unit
      - integration
    
    unit-tests:
      stage: unit
      script:
        - pytest tests/unit
      only:
        - merge_requests
    
    integration-tests:
      stage: integration
      script:
        - docker-compose up -d
        - pytest tests/integration
        - docker-compose down
      only:
        - main
  3. Adicione um gate de qualidade que falhe o pipeline se a cobertura de testes unitários for menor que 80%.
  4. ✓ Resposta:Adicione a opção --cov-fail-under=80 ao pytest:
    pytest tests/unit --cov=src --cov-fail-under=80
  5. Explique por que testes E2E não devem ser executados a cada commit.
  6. ✓ Resposta:Testes E2E são lentos (podem levar minutos) e frágeis (dependem de ambiente completo). Executá-los a cada commit atrasaria o feedback e poderia causar falsos positivos. Por isso, são executados apenas em branches principais ou antes do deploy.
  7. Crie um script Bash que execute testes unitários, de integração e E2E em sequência, parando se algum falhar.
  8. ✓ Resposta:
    #!/bin/bash
    set -e
    pytest tests/unit --junitxml=reports/unit.xml
    docker-compose -f docker-compose.test.yml up -d
    pytest tests/integration --junitxml=reports/integration.xml
    docker-compose -f docker-compose.test.yml down
    npx cypress run --reporter junit --reporter-options mochaFile=reports/e2e.xml
  9. Usando o SonarQube, explique como configurar um quality gate que exija cobertura mínima de 80%.
  10. ✓ Resposta:No SonarQube, vá em Quality Gates, crie um novo gate com a condição "Coverage" < 80% para falhar. Associe o gate ao projeto. No pipeline, execute o sonar-scanner e verifique o status com a API. Se o status não for OK, falhe o pipeline.