Integración CI/CD

Por: Artiko
gherkinci-cdgithub-actionsgitlabparalelizacion

Integración CI/CD

Esta sección cubre cómo poner Gherkin a correr automáticamente en pipelines, con paralelización, retries, gating y reportes accesibles para todo el equipo.

Workflow base: GitHub Actions + Cucumber-JS

# .github/workflows/bdd.yml
name: BDD Tests

on:
  push:
    branches: [main]
  pull_request:

jobs:
  bdd-smoke:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
      - run: npm ci
      - run: npx cucumber-js --tags "@smoke and not @wip"
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: cucumber-report-smoke
          path: reports/

  bdd-regression:
    runs-on: ubuntu-latest
    needs: bdd-smoke
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx cucumber-js --tags "@regression and not @wip"
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: cucumber-report-regression
          path: reports/

Patrón: smoke en cada push (rápido), regression solo en PRs (completo).

Paralelización

Cucumber-JS soporta paralelización built-in:

cucumber-js --parallel 4

Ejecuta cuatro workers en paralelo. Cada worker toma scenarios independientes.

Requisitos para paralelizar

Con behave: behavex o behave-parallel

pip install behavex
behavex --parallel-processes 4 --parallel-scheme scenario

--parallel-scheme scenario paraleliza por scenario (recomendado).

Sharding entre runners

Para tests muy largos, distribuir scenarios entre varias máquinas:

# .github/workflows/bdd.yml
jobs:
  bdd:
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx cucumber-js --parallel 2 --shard ${{ matrix.shard }}/4

Cucumber-JS no tiene --shard nativo todavía. Una solución: distribuir features manualmente:

jobs:
  bdd-auth:
    runs-on: ubuntu-latest
    steps:
      - run: npx cucumber-js features/auth/

  bdd-cart:
    runs-on: ubuntu-latest
    steps:
      - run: npx cucumber-js features/cart/

  bdd-billing:
    runs-on: ubuntu-latest
    steps:
      - run: npx cucumber-js features/billing/

Retry de scenarios flaky

Algunos tests son inestables (race conditions, network glitches). Cucumber-JS soporta --retry:

cucumber-js --retry 2

Reintenta hasta 2 veces los scenarios que fallan. Útil pero anti-patrón si lo aplicás a todo: enmascara tests realmente flaky.

Mejor: marcá los flaky con tag y retry solo esos.

cucumber-js --retry 2 --retry-tag-filter "@flaky"
@flaky
Scenario: Sometimes fails due to external API
  ...

Y mantené una lista visible de scenarios @flaky con un tracker para arreglarlos.

Gating por etapas

Estrategia clásica: smoke → integration → e2e en cascada.

jobs:
  smoke:
    runs-on: ubuntu-latest
    steps:
      - run: npx cucumber-js --tags "@smoke"

  integration:
    runs-on: ubuntu-latest
    needs: smoke
    steps:
      - run: npx cucumber-js --tags "@integration and not @smoke"

  e2e:
    runs-on: ubuntu-latest
    needs: integration
    if: github.event_name == 'pull_request'
    steps:
      - run: npx cucumber-js --tags "@e2e"

Si smoke falla, no corre el resto. Ahorra recursos y feedback rápido.

Tags por entorno

Distintos entornos pueden requerir distintos tests:

EntornoTags ejecutados
local (dev)@smoke and not @wip and not @manual
CI (PR)@smoke and @regression and not @wip
staging@e2e and not @local-only
production@production-safe and not @destructive
- run: npx cucumber-js --tags "${{ env.TAG_EXPRESSION }}"
  env:
    TAG_EXPRESSION: "@smoke and not @wip"

Servicios externos: docker-compose en CI

Si tus tests necesitan DB, queue, redis, etc:

jobs:
  bdd:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: test
          POSTGRES_DB: test
        ports: ['5432:5432']
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx cucumber-js
        env:
          DATABASE_URL: postgres://postgres:test@localhost:5432/test

GitHub Actions levanta el contenedor antes de los tests.

Reportes en PR comments

Postear el resumen en el PR para visibilidad:

- name: BDD report comment
  if: github.event_name == 'pull_request'
  uses: actions/github-script@v7
  with:
    script: |
      const fs = require('fs')
      const report = JSON.parse(fs.readFileSync('reports/cucumber.json', 'utf-8'))
      let total = 0, passed = 0, failed = 0
      for (const f of report) {
        for (const s of f.elements ?? []) {
          total++
          const ok = s.steps.every(st => st.result?.status === 'passed')
          if (ok) passed++; else failed++
        }
      }
      const body = `## BDD Report\n${passed}/${total} scenarios passed (${failed} failed)`
      await github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body
      })

Publicar living docs en GitHub Pages

deploy-living-docs:
  runs-on: ubuntu-latest
  needs: bdd
  if: github.ref == 'refs/heads/main'
  steps:
    - uses: actions/checkout@v4
    - uses: actions/download-artifact@v4
      with:
        name: cucumber-report-regression
        path: dist
    - uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./dist

Cada merge a main regenera y publica los reports en GitHub Pages.

GitLab CI

Equivalente para GitLab:

# .gitlab-ci.yml
stages:
  - test

variables:
  POSTGRES_DB: test
  POSTGRES_USER: test
  POSTGRES_PASSWORD: test

bdd:smoke:
  stage: test
  image: node:20
  services:
    - postgres:16
  script:
    - npm ci
    - npx cucumber-js --tags "@smoke and not @wip"
  artifacts:
    when: always
    paths:
      - reports/
    reports:
      junit: reports/junit.xml
  variables:
    DATABASE_URL: postgres://test:test@postgres:5432/test

bdd:regression:
  extends: bdd:smoke
  script:
    - npm ci
    - npx cucumber-js --tags "@regression"
  only:
    - merge_requests

Behave en CI (Python)

# .github/workflows/bdd-python.yml
name: BDD Tests Python

on: [push, pull_request]

jobs:
  bdd:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install -r requirements.txt
      - run: behave --tags=@smoke --tags=~@wip --format=junit --junit-directory=reports
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: behave-report
          path: reports/

Métricas y dashboard de equipo

Mantenés un dashboard con métricas que importan:

Herramientas:

Anti-patrones de CI/CD

1. Suite que tarda 45 minutos

Acción: paralelizar y filtrar por tags. Suite > 10 minutos pierde valor de feedback rápido.

2. Reintentar todo automáticamente

Acción: marcar @flaky y atacar la causa raíz.

3. Ignorar tests rotos

Acción: si un scenario está roto > 1 día, taggéalo @quarantined, abrí un ticket y arreglálo en sprint actual o eliminálo.

4. Reportes que solo CI ve

Acción: publicar como living docs en GitHub Pages o similar.

5. Sin gating en deploys

Acción: bloquear merges a main si los smoke tests fallan; bloquear deploy a producción si los e2e fallan.

Checklist de pipeline maduro

Diagrama: pipeline completo

flowchart TD
    Commit[git push] --> SmokeJob[Smoke job<br/>< 2 min]
    SmokeJob -->|pasa| RegressionJob[Regression<br/>solo en PRs<br/>< 15 min]
    SmokeJob -->|falla| Block[Bloquear merge]
    RegressionJob -->|pasa| MergeOK[Merge habilitado]
    MergeOK --> Main[Merge a main]
    Main --> E2EJob[E2E nightly<br/>contra staging]
    E2EJob -->|pasa| Deploy[Deploy a prod]
    E2EJob -->|falla| Alert[Alerta al equipo]
    Main --> LivingDocs[Regenerar living docs<br/>en GitHub Pages]

Resumen

Cierre del tutorial

Recorriste Gherkin de cero a hero: anatomía, primer escenario, Background, Outline, tags, Data Tables, step definitions en TS y Python, hooks, antipatrones, living docs y CI. Combinado con EARS para los requisitos, tenés un flujo completo de specification by example end-to-end.

Próximos pasos:

  1. Aplicá Gherkin a una feature real en tu proyecto
  2. Configurá el pipeline en GitHub Actions / GitLab CI
  3. Publicá living docs para tu equipo
  4. Combiná con EARS para trazabilidad requisito → scenario → código
  5. Ampliá a otras herramientas: Playwright, Selenium, integration testing en pytest

Gherkin es una inversión en comunicación tanto como en testing. Equipos que lo adoptan bien hablan el mismo idioma — y eso vale más que cualquier mejora puntual en cobertura.