CI/CD con Newman y GitHub Actions: del comando local al pipeline automático

Newman + GitHub Actions: el mismo comando local ahora corre en cada push. Reporte HTML en GitHub Pages. Primer push exitoso, 150 assertions, 48s.

Newman Run Dashboard en GitHub Pages: 6 iteraciones, 150 assertions, 2 failed tests, 72 requests, duración 10.7s, response time promedio 129ms
El reporte de Newman publicado automáticamente en GitHub Pages. 6 iteraciones del CSV, 150 assertions, 2 fallos intencionales.

Nota para el lector

El post anterior parametrizó el CRUD con CSV y Newman. Ahora ese mismo comando corre solo en cada push. Si querés ver el reporte en vivo → cesarbeassuarez.github.io/postman-api-testing.

Este post es parte de la serie de Postman.


Contexto

Tenía Newman corriendo local. Abría PowerShell, navegaba a la carpeta, ejecutaba el comando, esperaba, revisaba el reporte HTML. Funcionaba.

Pero era manual. Cada vez que cambiaba algo en la colección, tenía que acordarme de correr Newman. Y si no lo hacía, el reporte quedaba desactualizado.

Ya hice esto con Selenium. En el post de CI/CD del qa-automation-lab armé un pipeline con GitHub Actions que corre los 96 tests en cada push, genera el reporte Allure y lo publica en GitHub Pages. Ese proceso me llevó +15 commits arreglando problemas reales de CI.

Con Newman la promesa era más simple: no hay Maven, no hay Java, no hay Chrome headless. Es Node.js, un npm install y un comando.

Quería ver si era tan simple como parecía.


El comando que ya funcionaba local

Este era mi comando en PowerShell, corriendo desde la carpeta newman/:

newman run "Serenity Demo - Auth + Customer API Flow.postman_collection.json" -e "serenity-demo.postman_environment.json" -d "create_customer_data.csv" --folder Auth --folder "CRUD Happy Path" -r htmlextra

Qué hace:

  • Corre la colección exportada desde Postman
  • Usa el environment de Serenity Demo
  • Alimenta el CRUD con datos del CSV (6 escenarios)
  • Ejecuta solo las carpetas Auth y CRUD Happy Path
  • Genera un reporte HTML con htmlextra

El reporte caía en newman/newman/ con un nombre largo autogenerado. Para GitHub Pages necesitaba que caiga en una ruta predecible.


El workflow

Creé .github/workflows/newman-tests.yml:

name: Newman API Tests

on:
  push:
    branches: [ main ]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install Newman and HTML reporter
        run: |
          npm install -g newman
          npm install -g newman-reporter-htmlextra

      - name: Run Newman tests
        working-directory: newman
        run: |
          newman run "Serenity Demo - Auth + Customer API Flow.postman_collection.json" \
            -e "serenity-demo.postman_environment.json" \
            -d "create_customer_data.csv" \
            --folder Auth \
            --folder "CRUD Happy Path" \
            -r htmlextra \
            --reporter-htmlextra-export ./report/index.html

      - name: Upload report artifact
        if: always()
        uses: actions/upload-pages-artifact@v3
        with:
          path: newman/report

  deploy:
    needs: test
    if: always() && needs.test.result != 'cancelled'
    runs-on: ubuntu-latest

    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}

    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

Comparación con el pipeline de Selenium

El pipeline de Selenium necesitaba:

  • Java 17
  • Maven
  • Chrome headless instalado con setup-chrome
  • Configurar locale español y resolución 1920×1080 vía CDP
  • Generar el reporte Allure como paso separado
  • Timeout de 30s porque CI es más lento que local

El de Newman necesita:

  • Node.js
  • npm install -g newman newman-reporter-htmlextra
  • El mismo comando que corría local

Eso es todo. No hay browser, no hay driver, no hay build tool. Newman es un runner de terminal que hace requests HTTP. La complejidad está en la colección, no en el pipeline.

Lo que sí cambié respecto al comando local

Una cosa: el export del reporte.

Local, htmlextra generaba archivos con nombres como:

Serenity Demo - Auth + Customer API Flow-2026-03-22-04-32-07-899-0.html

Para GitHub Pages necesitaba un nombre fijo. Agregué:

--reporter-htmlextra-export ./report/index.html

Así el reporte siempre se llama index.html y GitHub Pages lo sirve directo en la raíz.

El if: always() y por qué importa

Dos líneas clave:

- name: Upload report artifact
  if: always()
deploy:
  needs: test
  if: always() && needs.test.result != 'cancelled'

Si Newman encuentra assertions fallidas, retorna exit code 1. GitHub Actions interpreta eso como fallo y no ejecuta los steps siguientes.

Pero mi colección tiene 2 fallos intencionales (del post de data-driven testing). Sin if: always(), el reporte nunca se publica.

Con if: always():

  • El step de upload corre aunque Newman falle
  • El job de deploy corre aunque el job de test falle
  • Pero no corre si alguien cancela el workflow manualmente (!= 'cancelled')

El reporte se publica siempre. El pipeline se muestra rojo si hay fallos. Las dos cosas son correctas.


Primera corrida

Pusheé. Fui a Actions. El workflow arrancó.

48 segundos después:

  • test: rojo (exit code 1)
  • deploy: verde
  • Reporte publicado en GitHub Pages

Entré a cesarbeassuarez.github.io/postman-api-testing:

  • 6 iteraciones
  • 150 assertions
  • 2 failed tests (los intencionales)
  • 72 requests
  • 10.7s de ejecución
  • Response time promedio: 129ms
Newman Run Dashboard en GitHub Pages: 6 iteraciones, 150 assertions, 2 failed tests, 72 requests, duración 10.7s, response time promedio 129ms
El reporte de Newman publicado automáticamente en GitHub Pages. 6 iteraciones del CSV, 150 assertions, 2 fallos intencionales.

Funcionó en el primer push.

Por qué dejé el pipeline rojo

Podría agregar || true al comando de Newman para que siempre retorne éxito. El pipeline se vería verde.

No lo hice.

Si un día Serenity Demo cae, o un endpoint cambia, o la colección se rompe por un cambio mío, quiero enterarme. Un pipeline verde con || true me oculta eso.

Los 2 fallos intencionales ya están explicados en el reporte HTML y en el README del repo. Quien entre al dashboard entiende qué falló y por qué.

Un pipeline rojo con explicación es más honesto que un pipeline verde que esconde información.

GitHub Actions job test mostrando el comando Newman ejecutado, exit code 1 por assertions fallidas, y upload del reporte completado
Newman corrió las 6 iteraciones. Exit code 1 por los 2 fallos intencionales. El upload del reporte corrió igual gracias al if: always().

Los warnings de Node.js 20

Aparecieron dos warnings:

Node.js 20 actions are deprecated. The following actions are running on Node.js 20
and may not work as expected: actions/checkout@v4, actions/setup-node@v4...
Actions will be forced to run with Node.js 24 by default starting June 2nd, 2025.
GitHub Actions Annotations mostrando error de exit code 1 en Run Newman tests y warning de deprecación de Node.js 20 en actions v4
Dos annotations: el exit code 1 de Newman (por los fallos intencionales) y el warning de Node.js 20 (cosmético, no afecta la ejecución).

No son errores. Las actions (checkout@v4, setup-node@v4, etc.) internamente usan Node.js 20 como runtime. GitHub está migrando a Node.js 24. Cuando actualicen las actions, los warnings desaparecen solos.

No afecta la ejecución. Newman corrió bien.


Dependencia externa: Serenity Demo

Hay una diferencia fundamental con el pipeline de Selenium.

En qa-automation-lab, los tests corren contra the-internet.herokuapp.com y SauceDemo. Son apps estables, siempre disponibles.

Acá los tests corren contra Serenity Demo, una app en Azure que no controlo. Si está caída o lenta, el pipeline falla.

No por un bug en mi colección. Por un problema de infraestructura externa.

Esto es un problema real de CI contra apps que no controlás. No tiene solución elegante: o aceptás que a veces falla por causas externas, o montás un mock server (que elimina el valor de testear contra una app real).

Por ahora lo acepto. Si en el futuro se vuelve inestable, evaluaré alternativas.


Estado actual

El repo postman-api-testing ahora tiene:

La serie de Postman tiene pipeline. El item del roadmap en el README ya se puede tachar.


Comparando los dos pipelines

Armé CI/CD para dos repos distintos:

  • qa-automation-lab: Selenium + Java + TestNG + Allure + Chrome headless → +15 commits
  • postman-api-testing: Newman + htmlextra → funcionó en el primer push

La diferencia no es que uno sea mejor. Es que son problemas distintos.

Selenium necesita un browser real corriendo en un servidor. Eso implica drivers, resolución, locale, timeouts ajustados para CI. Cada variable es un punto de fallo.

Newman hace requests HTTP. No hay browser, no hay UI, no hay driver. La complejidad está en la colección y en la app externa, no en el pipeline.

Si tu automation es UI → prepárate para iterar el pipeline. Si tu automation es API → el pipeline es lo más simple de todo.