← Back to list

Automatizando pruebas de API con Newman desde GitHub Actions

🔍 Introducción

David Melchor · 2026-03-07 06:01 · 0 claps · 7.1 min read
#newman #postman #github-actions #docker #continous-integration
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud 🔓 · Open Source

Automatizando pruebas de API con Newman desde GitHub Actions

🔍 Introducción

En equipos de desarrollo, las pruebas de API se ejecutan de forma manual desde Postman. Es útil en etapas iniciales, pero se convierte en un riesgo cuando el proyecto crece y los despliegues suelen ser más frecuentes.

Si cada cambio en el código depende de ejecutar la colección de forma manual, es posible que en ocasiones se pase por alto y los tests no se realicen.

En este artículo ejecutaremos una colección de Postman utilizando Newman directamente desde GitHub Actions, integrando las pruebas dentro de un pipeline de CI.

⚙️ ¿Qué vamos a construir?

  • Creación de archivo YAML para ejecución de repositorio de test
  • Colección de Postman
  • Variable de entorno en Postman

🖥️ Tecnologías utilizadas

  • Newman: Es un ejecutor de colecciones de Postman desde línea de comandos.
  • Postman: Es una plataforma unificada para probar, documentar y supervisar API.
  • GitHub Actions: Es una plataforma de integración y despliegue continuo (CI/CD) que permite automatizar flujos de trabajo de desarrollo de software.
  • Docker: Es una plataforma de código abierto que permite empaquetar aplicaciones y sus dependencias dentro de contenedores.

🏗️ Diagrama de la solución

🧱 Estructura de repositorio de Newman

newman-tests/
├── .github/
│   └── workflows/
│       └── trigger-api.yaml        # Configuración del pipeline de CI
├── docs/
│   └── index.html                  # Redirecciona el reporte a index (GitHub Pages)
├── postman/
│   └── collection.json             # Colección de Postman con los request
│   └── environment.json            # Variable utilizada por Postman
├── results/
│   └── run_04032026_101702         # Carpeta generada por cada ejecución
│       └── report.html             # Configuración del pipeline de CI
├── scripts/
│   └── execute-tests.py            # Script de Python que ejecuta Newman
├── .dockerignore                   # Exclusión de directorios, archivos
├── .gitignore                      # Exclusión de directorios, archivos para Git
├── docker-compose.yaml             # Definición de servicios
├── Dockerfile                      # Configuración para construir imagen
├── README.md                       # Información del proyecto
├── requirements.txt                # Dependencias del proyecto

🔧 Configuración inicial

  • Creación de entorno virtual
python -m venv venv
En Linux: source venv/bin/activate  
En Windows: venv\Scripts\activate

Para desactivar el ambiente, utilizar "deactivate"
  • Instalación de dependencias
# Instalación de newman
npm install -g newman

# Reporteria
npm install -g newman-reporter-htmlextra

En el archivo trigger-api.yaml, copiar y pegar el siguiente código

name: Postman API Tests

on:
  workflow_dispatch:
    inputs:
      base_url:
        description: 'Base URL de la API'
        required: false
        default: '**URL DE RENDER**'

permissions:
  contents: write

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout código
      uses: actions/checkout@v4

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

    - name: Setup Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.x'

    - name: Instalar Newman y reporter
      run: |
        npm install -g newman
        npm install -g newman-reporter-htmlextra

    - name: Verificar instalación
      run: newman -v

    - name: Ejecutar tests de Postman
      id: run_tests
      continue-on-error: true
      env:
        BASE_URL: ${{ github.event.inputs.base_url || 'http://localhost:8000' }}
        TZ: America/Guatemala
      run: python scripts/execute-tests.py --base-url=$BASE_URL

    - name: Obtener timestamp de la ejecución
      id: get_timestamp
      if: always()
      run: |
        if [ -f docs/.last_run.txt ]; then
          TIMESTAMP=$(cat docs/.last_run.txt)
        else
          TIMESTAMP=$(date +%Y%m%d_%H%M%S)
        fi
        echo "timestamp=$TIMESTAMP" >> $GITHUB_OUTPUT

    - name: Subir reporte como artefacto
      if: always()
      uses: actions/upload-artifact@v4
      with:
        name: run_${{ steps.get_timestamp.outputs.timestamp }}
        path: results/run_${{ steps.get_timestamp.outputs.timestamp }}/
        retention-days: 30

    - name: 💾 Commit report to repository
      if: always()
      run: |
        git config user.name "github-actions"
        git config user.email "actions@github.com"

        git add docs/

        if git diff --staged --quiet; then
          echo "No changes to commit"
        else
          git commit -m "Update Newman report - run ${{ github.run_number }}"
          git push
        fi

    - name: 🧹 Limpiar Docker
      if: always()
      run: docker system prune -f

Colocar el siguiente script en las request de Postman antes de exportarlas

  • Pestaña de Scripts
# Primer Request

// Validaciones básicas
// Primero parsear la respuesta
const json = pm.response.json();

// Status correcto
pm.test("Status code es 201 Created", function () {
    pm.response.to.have.status(201);
});

// Tiempo de respuesta
pm.test("Respuesta menor a 500ms", function () {
    pm.expect(pm.response.responseTime).to.be.below(500);
});

// Formato JSON
pm.test("Response es JSON", function () {
    pm.response.to.be.json;
});

// Validaciones de estructura
// Luego hacer las validaciones con la estructura correcta
pm.test("Tiene exactamente los campos esperados", function () {
    const expectedKeys = ["id", "title", "description", "created_at", "updated_at"];
    const actualKeys = Object.keys(json);
    pm.expect(actualKeys.sort()).to.eql(expectedKeys.sort());
});

// Validación de timestamps
pm.test("Created tiene formato fecha", function () {
    pm.expect(new Date(json.created_at).toString()).not.eql("Invalid Date");
});

pm.test("Updated tiene formato fecha", function () {
    pm.expect(new Date(json.updated_at).toString()).not.eql("Invalid Date");
});

// Validación de negocio
pm.test("ID es número", function () {
    pm.expect(json.id).to.be.a("number");
});

pm.test("Title no está vacío", function () {
    pm.expect(json.title.length).to.be.above(0);
});

// Validación de schema

const schema = {
    "type": "object",
    "required": ["id", "title", "description", "created_at", "updated_at"],
    "properties": {
        "id": { "type": "number" },
        "title": { "type": "string" },
        "description": { "type": "string" },
        "created_at": { "type": "string" },
        "updated_at": { "type": "string" }
    }
};

pm.test("Schema válido", function () {
    pm.response.to.have.jsonSchema(schema);
});

// Validación de Headers

pm.test("Content-Type correcto", function () {
    pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");
});

# Segundo Request

pm.test("Error por campo obligatorio", function () {
    pm.response.to.have.status(422);
});

# Tercer Request

pm.test("No permite title vacío", function () {
    pm.response.to.have.status(422);
});

# Cuarto Request

pm.test("Tipo inválido", function () {
    pm.response.to.have.status(422);
});

Crear la variable de entorno para utilizar en Postman

  • La variable debe llamarse “base_url”, siempre en minúsculas
  • Se utiliza de esa forma por buenas prácticas

En el archivo execute-tests.py, copiar y pegar el siguiente código

import time
import subprocess
import os
import sys
import shutil
import platform

# Creación de timestamp
timestamp = time.strftime("%Y%m%d_%H%M%S")

# Definir directorios
results_dir = f"results/run_{timestamp}"
docs_dir = "docs"

os.makedirs(results_dir, exist_ok=True)
os.makedirs(docs_dir, exist_ok=True)

# BASE_URL por defecto (prioridad: CLI > ENV > default)
base_url = os.getenv("BASE_URL", "** URL DE RENDER **")

# Leer argumento desde CLI (CI)
for arg in sys.argv:
    if arg.startswith("--base-url="):
        base_url = arg.split("=", 1)[1]

print(f"🌐 Ejecutando pruebas contra: {base_url}")

# Definir rutas de archivos de salida
report_file = f"{results_dir}/report.html"

# Ejecutar Newman
command = [
    "newman", "run",
    "postman/Endpoints Fast API.postman_collection.json",
    "-e", "postman/environment.json",
    "--env-var", f"base_url={base_url}",
    "--reporters", "htmlextra",
    "--reporter-htmlextra-export", report_file,
    "--timeout-request", "10000",  # 10 segundos por request
    "--delay-request", "500"  # 500ms entre requests
]

# En Windows, usar shell=True para encontrar newman.cmd
is_windows = platform.system() == "Windows"
result = subprocess.run(command, capture_output=True, text=True, shell=is_windows)

print(result.stdout)
if result.stderr:
    print(result.stderr)

# Almacenar el timestamp siempre (incluso si Newman falla)
with open(f"{docs_dir}/.last_run.txt", "w") as f:
    f.write(timestamp)

# Verificar que el reporte fue generado
if not os.path.exists(report_file):
    print(f"❌ Error: No se generó el archivo de reporte en {report_file}")
    print("Verifica que Newman se ejecutó correctamente")
    # Crear un reporte básico de error
    error_html = f"""<!DOCTYPE html>
<html>
<head><title>Error en ejecución</title></head>
<body>
<h1>Error en la ejecución de Newman</h1>
<p>No se pudo generar el reporte. Código de salida: {result.returncode}</p>
<pre>{result.stdout}</pre>
<pre>{result.stderr}</pre>
</body>
</html>"""
    with open(report_file, "w") as f:
        f.write(error_html)

# Copiar a docs/ para GitHub Pages (siempre, incluso si falló)
shutil.copy(report_file, f"{docs_dir}/index.html")

if result.returncode != 0:
    print(f"❌ Newman falló con código de salida: {result.returncode}")
    print(f"📁 Resultados guardados en: {results_dir}")
    print("📄 Report publicado en GitHub Pages")
    sys.exit(result.returncode)

# Mensajes informativos
print("✅ Newman ejecución completada correctamente")
print(f"📁 Resultados guardados en: {results_dir}")
print("📄 Report publicado correctamente en GitHub Pages")

En el archivo .dockerignore, copiar y pegar el siguiente código

# Git
.git
.gitignore
.github

# Python
__pycache__
*.py[cod]
*$py.class
*.so
.Python
*.egg-info
.installed.cfg
*.egg

# Virtual Environment
venv/
ENV/
env/

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# Results (se generan en runtime)
results/

# Documentation
README.md
docs/

# Docker
Dockerfile
docker-compose.yml
.dockerignore

# OS
.DS_Store
Thumbs.db

# Logs
*.log

En el archivo .gitignore, copiar y pegar el siguiente código

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Virtual Environment
venv/
ENV/
env/

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# Testing
.pytest_cache/
.coverage
htmlcov/
*.cover

# OS
.DS_Store
Thumbs.db

# Logs
*.log

En el archivo docker-compose.yaml, copiar y pegar el siguiente código

version: '3.8'

services:
  postman-tests:
    build: .
    container_name: postman-tests
    environment:
      - base_url=${base_url:-** URL DE RENDER **}
    volumes:
      - ./results:/app/results
      - ./docs:/app/docs
    command: python3 scripts/execute-tests.py

En el archivo Dockerfile, copiar y pegar el siguiente código

# Usar imagen base de Node.js (Newman requiere Node)
FROM node:18-alpine

# Instalar Python para el script de ejecución
RUN apk add --no-cache python3 py3-pip

# Crear directorio de trabajo
WORKDIR /app

# Instalar Newman y el reporter
RUN npm install -g newman newman-reporter-htmlextra

# Copiar archivos del proyecto
COPY postman/ ./postman/
COPY scripts/ ./scripts/
COPY docs/ ./docs/

# Crear directorios necesarios
RUN mkdir -p results docs

# Variable de entorno por defecto
ENV base_url=** URL DE RENDER **

# Comando por defecto
CMD ["python3", "scripts/execute-tests.py"]

✅ Ejecución de la colección desde Postman

  • Request creación de tareas exitosa

  • Request creación de tareas sin campo title

  • Request creación de tareas con title vacío

  • Request creación de tareas con campo invalido

✅ Ejecución desde consola con Newman

  • Al ejecutarlo desde la CLI local utilizaremos la URL de Render

  • Abrimos el reporte HTML y validamos que la ejecución tenga todas las pruebas passed

✅ Ejecución desde GitHub Actions

  • Dar clic en Actions
  • Dar clic en Postman API Tests
  • Dar clic en Run workflow

  • Al finalizar la ejecución se publicará el reporte en GitHub Pages

  • Ingresar a la ejecución para descargar el artefacto y analizarlo localmente

Con este ejemplo práctico pueden seguir explorando sobre pruebas de API.


메타데이터
post_id
33c1b2c5c2d8
slug
automatizando-pruebas-de-api-con-newman-desde-github-actions-33c1b2c5c2d8
url
https://medium.com/@dmelchornatplat/automatizando-pruebas-de-api-con-newman-desde-github-actions-33c1b2c5c2d8
canonical_url
https://medium.com/@dmelchornatplat/automatizando-pruebas-de-api-con-newman-desde-github-actions-33c1b2c5c2d8
author_url
https://medium.com/@dmelchornatplat
status
ok
fetched_at
2026-07-13 06:23:13