Por qué GitHub Actions para QA

GitHub Actions es la plataforma CI/CD integrada de GitHub. Si tu proyecto vive en GitHub, Actions elimina la necesidad de servicios CI/CD externos. Los workflows se ejecutan directamente dentro de GitHub, con integración estrecha con pull requests, issues y el ecosistema de GitHub.

Para ingenieros QA, esta integración estrecha es una gran ventaja. Los resultados de tests aparecen directamente en pull requests. Los checks fallidos bloquean merges. Los artefactos de tests son accesibles desde la misma interfaz donde revisas código.

Fundamentos de Workflows

Un workflow de GitHub Actions es un archivo YAML en .github/workflows/. Aquí hay un workflow mínimo que ejecuta tests en cada push:

name: Tests

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm test

Triggers

GitHub Actions soporta muchos eventos trigger:

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * *'    # Diario a las 6 AM UTC
  workflow_dispatch:         # Trigger manual desde UI

Para QA, los triggers más importantes son:

  • pull_request: Ejecutar tests en cada PR para detectar problemas antes del merge
  • push a main: Ejecutar regresión completa después del merge para verificar la rama principal
  • schedule: Suites de tests nocturnos completos, tests de performance o escaneos de seguridad

Jobs y Steps

Un workflow contiene uno o más jobs. Cada job se ejecuta en una máquina virtual limpia (runner). Los jobs se ejecutan en paralelo por defecto pero pueden depender entre sí:

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run test:unit

  e2e-tests:
    needs: unit-tests    # Se ejecuta después de que unit-tests complete
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test

Estrategia Matrix para Testing Cross-Browser

Las estrategias matrix son una de las funcionalidades más poderosas de GitHub Actions para QA. Te permiten ejecutar la misma suite de tests en múltiples configuraciones automáticamente:

jobs:
  e2e-tests:
    strategy:
      matrix:
        browser: [chromium, firefox, webkit]
        os: [ubuntu-latest, windows-latest]
      fail-fast: false
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx playwright install --with-deps ${{ matrix.browser }}
      - run: npx playwright test --project=${{ matrix.browser }}

Esto crea 6 jobs paralelos (3 navegadores x 2 sistemas operativos). Configurar fail-fast: false asegura que todas las combinaciones se ejecuten incluso si una falla — quieres conocer todos los fallos, no solo el primero.

Sharding de Suites de Tests Grandes

Para suites de tests grandes, puedes dividir tests entre múltiples runners:

jobs:
  e2e-tests:
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test --shard=${{ matrix.shard }}/4

Esto divide la suite de tests en 4 partes iguales ejecutándose en paralelo. Una suite de 40 minutos se convierte en 10 minutos.

Artefactos y Reportes de Tests

Subiendo Artefactos de Tests

Guarda reportes de tests, capturas de pantalla y videos de las ejecuciones:

- name: Ejecutar tests E2E
  run: npx playwright test
  continue-on-error: true

- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: test-results
    path: |
      playwright-report/
      test-results/
    retention-days: 14

La condición if: always() asegura que los artefactos se suban incluso cuando los tests fallan — es cuando más los necesitas.

Publicando Resultados de Tests en PRs

Usa acciones de terceros para publicar resultados de tests como comentarios en PRs:

- name: Publicar Resultados de Tests
  uses: dorny/test-reporter@v1
  if: always()
  with:
    name: Tests Playwright
    path: test-results/junit.xml
    reporter: java-junit

Esto crea un check run con resultados detallados de tests visibles directamente en el pull request.

Caché para Builds más Rápidos

Cachear dependencias reduce dramáticamente el tiempo del pipeline:

- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'npm'

# Para navegadores de Playwright
- name: Cachear navegadores Playwright
  uses: actions/cache@v4
  with:
    path: ~/.cache/ms-playwright
    key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

- name: Instalar Playwright
  run: npx playwright install --with-deps

Sin caché, npm ci y la instalación de navegadores pueden tomar 2-3 minutos. Con caché, toman segundos.

Secrets y Variables de Entorno

jobs:
  test:
    runs-on: ubuntu-latest
    env:
      BASE_URL: https://staging.example.com
      NODE_ENV: test
    steps:
      - run: npm run test:e2e
        env:
          API_KEY: ${{ secrets.STAGING_API_KEY }}
          DB_URL: ${{ secrets.TEST_DATABASE_URL }}

Los secrets se configuran en los ajustes del repositorio (Settings > Secrets and variables > Actions). Se enmascaran en los logs y nunca se exponen en archivos de workflow.

Ejercicio: Construye un Workflow QA Completo

Crea un workflow de GitHub Actions que:

  1. Ejecute tests unitarios en cada push
  2. Ejecute tests E2E en 3 navegadores en pull requests
  3. Suba reportes de tests como artefactos
  4. Publique resultados de tests como check en PR
  5. Ejecute una regresión nocturna completa
Solución
name: QA Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 2 * * *'
  workflow_dispatch:

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm run test:unit -- --coverage --ci
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: coverage-report
          path: coverage/
          retention-days: 7

  e2e-tests:
    if: github.event_name == 'pull_request' || github.event_name == 'schedule'
    needs: unit-tests
    strategy:
      matrix:
        browser: [chromium, firefox, webkit]
      fail-fast: false
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - name: Cachear Playwright
        uses: actions/cache@v4
        with:
          path: ~/.cache/ms-playwright
          key: pw-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
      - run: npx playwright install --with-deps ${{ matrix.browser }}
      - run: npx playwright test --project=${{ matrix.browser }}
        env:
          BASE_URL: ${{ secrets.STAGING_URL }}
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results-${{ matrix.browser }}
          path: |
            playwright-report/
            test-results/
          retention-days: 14
      - name: Reporte de Tests
        uses: dorny/test-reporter@v1
        if: always()
        with:
          name: E2E ${{ matrix.browser }}
          path: test-results/junit.xml
          reporter: java-junit

  nightly-regression:
    if: github.event_name == 'schedule'
    needs: e2e-tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm run test:regression
      - run: npm run test:performance

Workflows Reutilizables

Si múltiples repositorios necesitan el mismo pipeline de tests, crea workflows reutilizables:

# .github/workflows/reusable-e2e.yml
name: E2E Tests Reutilizables

on:
  workflow_call:
    inputs:
      base-url:
        required: true
        type: string
      browsers:
        required: false
        type: string
        default: '["chromium"]'
    secrets:
      api-key:
        required: true

jobs:
  e2e:
    strategy:
      matrix:
        browser: ${{ fromJson(inputs.browsers) }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx playwright test --project=${{ matrix.browser }}
        env:
          BASE_URL: ${{ inputs.base-url }}
          API_KEY: ${{ secrets.api-key }}

GitHub Actions vs Jenkins: Perspectiva QA

AspectoGitHub ActionsJenkins
SetupSin setup para repos de GitHubRequiere instalación y mantenimiento del servidor
CostoGratis para repos públicos; minutos pagos para privadosGratis pero pagas por servidores
FlexibilidadBuena con acciones del marketplaceMáxima con más de 1800 plugins
Soporte containersDocker nativo y service containersVía plugin Docker Pipeline
Integración PRNativa — checks, status, comentariosRequiere plugins y webhooks
Self-hosted runnersSoportadoFuncionalidad core
Matrix buildsFirst-class con strategy.matrixRequiere scripted pipeline
Gestión de secretsSecrets de repositorio/org integradosPlugin Credentials

Mejores Prácticas

  1. Usa actions/checkout@v4 con fetch-depth: 0 si tus tests dependen del historial de git (para detección de cambios o blame).

  2. Configura fail-fast: false en estrategias matrix para jobs de test. Quieres ver todos los fallos, no solo el primero.

  3. Cachea agresivamente — dependencias npm, navegadores de Playwright y cualquier otro recurso descargable.

  4. Usa continue-on-error: true en steps de test combinado con if: always() en upload de artefactos. Esto asegura que los reportes de tests siempre estén disponibles.

  5. Fija versiones de acciones a hashes SHA específicos en producción, no solo versiones mayores. Esto previene ataques de cadena de suministro.

  6. Usa grupos de concurrencia para cancelar ejecuciones de workflow obsoletas cuando se pushea un nuevo commit:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true