Introducción al Registro de Ejecución de Pruebas

Los registros de ejecución de pruebas son la base de la documentación de aseguramiento de calidad, proporcionando un registro completo de actividades de prueba, resultados y evidencias. Estos registros sirven como documentación legal, recursos de depuración y registros históricos que permiten a los equipos reproducir problemas, analizar tendencias y demostrar cumplimiento con estándares de calidad.

Un registro de ejecución de pruebas bien estructurado transforma actividades de prueba efímeras en documentación permanente y procesable que agrega valor durante todo el ciclo de vida del desarrollo de software.

Componentes Principales de los Registros de Ejecución

Elementos Esenciales del Registro

Cada registro de ejecución de pruebas debe capturar la siguiente información crítica:

Metadatos de Ejecución:

  • ID de ejecución único
  • Identificador de caso de prueba
  • Marca de tiempo de ejecución (inicio y fin)
  • Identificación del probador
  • Detalles del entorno de prueba
  • Información de compilación/versión

Resultados de Ejecución:

  • Estado Pass/Fail/Blocked/Skip
  • Resultados reales vs. esperados
  • Referencias de defectos
  • Duración de ejecución
  • Intentos de reintento y resultados

Contexto Ambiental:

  • Sistema operativo y versión
  • Versión del navegador/aplicación
  • Estado de la base de datos
  • Configuración de red
  • Disponibilidad de servicios de terceros

Estructura de Ejemplo de Registro de Ejecución

{
  "executionId": "EXEC-20250108-001",
  "testCaseId": "TC-AUTH-015",
  "testCaseName": "Inicio de Sesión de Usuario con Credenciales Válidas",
  "executionTime": {
    "start": "2025-01-08T10:30:00Z",
    "end": "2025-01-08T10:32:15Z",
    "duration": 135
  },
  "executor": {
    "name": "Sarah Johnson",
    "role": "Ingeniera QA",
    "id": "sjohnson@company.com"
  },
  "environment": {
    "name": "Staging",
    "url": "https://staging.app.com",
    "buildVersion": "v2.4.1-RC3",
    "os": "Windows 11 Pro",
    "browser": "Chrome 120.0.6099.109"
  },
  "status": "PASSED",
  "steps": [
    {
      "stepNumber": 1,
      "description": "Navegar a la página de inicio de sesión",
      "expected": "Formulario de inicio de sesión mostrado",
      "actual": "Formulario de inicio de sesión mostrado correctamente",
      "status": "PASSED",
      "screenshot": "step1_login_page.png"
    },
    {
      "stepNumber": 2,
      "description": "Ingresar nombre de usuario 'testuser@example.com'",
      "expected": "Campo de nombre de usuario poblado",
      "actual": "Campo de nombre de usuario poblado",
      "status": "PASSED"
    },
    {
      "stepNumber": 3,
      "description": "Ingresar contraseña",
      "expected": "Contraseña enmascarada con puntos",
      "actual": "Contraseña enmascarada con puntos",
      "status": "PASSED"
    },
    {
      "stepNumber": 4,
      "description": "Hacer clic en botón 'Iniciar Sesión'",
      "expected": "Redirección al panel en 2 segundos",
      "actual": "Redirigido al panel en 1.3 segundos",
      "status": "PASSED",
      "screenshot": "step4_dashboard.png",
      "performanceMetric": 1.3
    }
  ],
  "evidence": {
    "screenshots": ["step1_login_page.png", "step4_dashboard.png"],
    "videos": ["full_execution.mp4"],
    "logs": ["browser_console.log", "network_traffic.har"]
  },
  "notes": "Ejecución completada sin problemas. Rendimiento dentro del rango aceptable."
}

Estrategias de Recopilación de Evidencias

Gestión de Capturas de Pantalla

Las capturas de pantalla son evidencia visual crítica que captura el estado de la aplicación en momentos específicos:

Mejores Prácticas:

  • Capturar capturas de pantalla en puntos de decisión y pasos de verificación
  • Usar convenciones de nomenclatura consistentes: {executionId}_{stepNumber}_{descripción}.png
  • Incluir capturas de pantalla de página completa para contexto
  • Anotar capturas de pantalla con resaltados para defectos
  • Almacenar con metadatos (marca de tiempo, resolución, navegador)

Herramientas Automatizadas de Captura de Pantalla:

# Ejemplo de captura de pantalla con Selenium WebDriver
from selenium import webdriver
from datetime import datetime
import os

def capture_evidence_screenshot(driver, execution_id, step_number, description):
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"{execution_id}_step{step_number}_{description}_{timestamp}.png"
    filepath = os.path.join("evidence", "screenshots", filename)

    # Asegurar que el directorio existe
    os.makedirs(os.path.dirname(filepath), exist_ok=True)

    # Capturar captura de pantalla de página completa
    driver.save_screenshot(filepath)

    # Registrar metadatos de captura de pantalla
    metadata = {
        "filename": filename,
        "timestamp": timestamp,
        "viewport": driver.get_window_size(),
        "url": driver.current_url,
        "step": step_number
    }

    return filepath, metadata

# Uso en prueba
driver = webdriver.Chrome()
driver.get("https://example.com/login")
screenshot_path, metadata = capture_evidence_screenshot(
    driver, "EXEC-001", 1, "login_page"
)

Grabación de Video para Escenarios Complejos

Las grabaciones de video proporcionan evidencia completa para escenarios de prueba complejos:

# Plugin de Pytest para grabación de video automática
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

@pytest.fixture
def video_recording_driver(request):
    chrome_options = Options()
    chrome_options.add_argument("--disable-dev-shm-usage")

    # Habilitar grabación de video a través de capacidades del navegador
    chrome_options.set_capability("goog:loggingPrefs", {"performance": "ALL"})

    driver = webdriver.Chrome(options=chrome_options)

    # Iniciar grabación de pantalla
    test_name = request.node.name
    video_path = f"evidence/videos/{test_name}.webm"

    yield driver

    # Guardar grabación al completar la prueba
    driver.quit()

    # Archivar video con resultado de prueba
    if request.node.rep_call.failed:
        # Mantener video para pruebas fallidas
        print(f"Prueba fallida - video guardado: {video_path}")
    else:
        # Opcional: eliminar videos de pruebas pasadas para ahorrar espacio
        pass

def test_checkout_process(video_recording_driver):
    driver = video_recording_driver
    # Implementación de prueba
    pass

Recopilación de Archivos de Registro

La recopilación completa de registros asegura reproducibilidad y capacidad de depuración:

Tipos de Registros a Recopilar:

Tipo de RegistroPropósitoMétodo de Recopilación
Consola del NavegadorErrores JavaScript, advertenciasdriver.get_log('browser')
Tráfico de RedLlamadas API, tiempos de respuestaExportación de archivo HAR
Registros de AplicaciónErrores backend, trazas de pilaHerramientas de agregación de registros
Consultas de Base de DatosOperaciones de datos, rendimientoRegistro de consultas
Registros del ServidorProblemas de infraestructuraRegistro centralizado (ELK, Splunk)
# Recopilación completa de registros
def collect_execution_evidence(driver, execution_id):
    evidence = {
        "browser_console": [],
        "network_traffic": None,
        "performance_metrics": {}
    }

    # Recopilar registros de consola del navegador
    for entry in driver.get_log('browser'):
        evidence["browser_console"].append({
            "timestamp": entry['timestamp'],
            "level": entry['level'],
            "message": entry['message']
        })

    # Recopilar métricas de rendimiento
    navigation_timing = driver.execute_script(
        "return window.performance.timing"
    )
    evidence["performance_metrics"] = {
        "page_load_time": navigation_timing['loadEventEnd'] - navigation_timing['navigationStart'],
        "dom_content_loaded": navigation_timing['domContentLoadedEventEnd'] - navigation_timing['navigationStart'],
        "first_paint": navigation_timing['responseStart'] - navigation_timing['navigationStart']
    }

    # Exportar tráfico de red (requiere Chrome DevTools Protocol)
    # Usando Chrome DevTools Protocol para exportación HAR
    evidence["network_traffic"] = export_network_har(driver)

    # Guardar paquete de evidencias
    evidence_path = f"evidence/{execution_id}/logs.json"
    with open(evidence_path, 'w') as f:
        json.dump(evidence, f, indent=2)

    return evidence

Documentación de Detalles del Entorno

Captura del Estado Completo del Entorno

El contexto ambiental es crucial para reproducir resultados de pruebas:

import platform
import psutil
import subprocess

def capture_environment_details():
    env_details = {
        "system": {
            "os": platform.system(),
            "os_version": platform.version(),
            "architecture": platform.machine(),
            "processor": platform.processor(),
            "python_version": platform.python_version()
        },
        "hardware": {
            "cpu_cores": psutil.cpu_count(logical=False),
            "cpu_threads": psutil.cpu_count(logical=True),
            "memory_total_gb": round(psutil.virtual_memory().total / (1024**3), 2),
            "memory_available_gb": round(psutil.virtual_memory().available / (1024**3), 2)
        },
        "network": {
            "hostname": platform.node(),
            "ip_addresses": get_ip_addresses()
        },
        "dependencies": get_installed_packages(),
        "browser_versions": get_browser_versions()
    }

    return env_details

def get_browser_versions():
    versions = {}

    # Versión de Chrome
    try:
        result = subprocess.run(
            ['google-chrome', '--version'],
            capture_output=True,
            text=True
        )
        versions['chrome'] = result.stdout.strip()
    except:
        versions['chrome'] = 'No instalado'

    # Versión de Firefox
    try:
        result = subprocess.run(
            ['firefox', '--version'],
            capture_output=True,
            text=True
        )
        versions['firefox'] = result.stdout.strip()
    except:
        versions['firefox'] = 'No instalado'

    return versions

Matriz de Comparación de Entornos

Cuando las pruebas fallan en un entorno pero pasan en otro, la comparación sistemática es esencial:

ComponenteEntorno de DesarrolloEntorno de StagingEntorno de Producción
Versión de Aplicaciónv2.4.1-devv2.4.1-RC3v2.4.0
Versión de Base de DatosPostgreSQL 15.3PostgreSQL 15.3PostgreSQL 15.2
SOUbuntu 22.04Ubuntu 22.04Ubuntu 20.04
Node.jsv20.10.0v20.10.0v18.18.0
Redis7.2.07.2.07.0.11
Balanceador de CargaNingunoNginx 1.24Nginx 1.22

Asegurando la Reproducibilidad de Pruebas

Lista de Verificación de Reproducibilidad

Una ejecución de prueba es reproducible cuando otro probador puede seguir el registro y lograr resultados idénticos:

Documentación de Requisitos Previos:

  1. Scripts de configuración de datos de prueba
  2. Procedimientos de poblamiento de base de datos
  3. Estados de archivos de configuración
  4. Configuraciones simuladas de servicios de terceros
  5. Dependencias de hora/fecha (si aplica)

Guía Paso a Paso de Reproducibilidad:

# Guía de Reproducibilidad de Prueba: EXEC-20250108-001

## Requisitos Previos
1. Entorno: Staging (https://staging.app.com)
2. Versión de Compilación: v2.4.1-RC3
3. Datos de Prueba: Cuenta de usuario testuser@example.com (contraseña en vault)
4. Estado de Base de Datos: Ejecutar script de poblamiento `db/seeds/auth_test_data.sql`

## Configuración del Entorno
```bash
# Clonar repositorio
git clone https://github.com/company/app.git
cd app
git checkout v2.4.1-RC3

# Instalar dependencias
npm install

# Configurar entorno
cp .env.staging .env
# Actualizar DATABASE_URL en .env

# Poblar datos de prueba
psql -U postgres -d app_staging -f db/seeds/auth_test_data.sql

Pasos de Ejecución de Prueba

  1. Navegar a https://staging.app.com/login
  2. Verificar que el formulario de inicio de sesión muestre campos de email y contraseña
  3. Ingresar email: testuser@example.com
  4. Ingresar contraseña: [del vault]
  5. Hacer clic en botón “Iniciar Sesión”
  6. Verificar redirección al panel en 2 segundos
  7. Verificar que el nombre de usuario “Test User” aparezca en el encabezado

Resultados Esperados

  • Todos los pasos pasan
  • El panel carga en < 2 segundos
  • No hay errores en consola
  • Cookie de sesión establecida con expiración de 24 horas

Limpieza

# Eliminar datos de prueba
psql -U postgres -d app_staging -f db/seeds/cleanup_auth_test_data.sql

### Pruebas Automatizadas de Reproducibilidad

```python
# Marco de validación de reproducibilidad
class ReproducibilityValidator:
    def __init__(self, original_execution_log):
        self.original = original_execution_log
        self.reproduction_attempts = []

    def attempt_reproduction(self, max_attempts=3):
        for attempt in range(max_attempts):
            print(f"Intento de reproducción {attempt + 1}/{max_attempts}")

            # Configurar entorno
            self.setup_environment(self.original['environment'])

            # Ejecutar caso de prueba
            result = self.execute_test_case(self.original['testCaseId'])

            # Comparar resultados
            comparison = self.compare_results(self.original, result)

            self.reproduction_attempts.append({
                "attempt": attempt + 1,
                "result": result,
                "comparison": comparison,
                "is_reproducible": comparison['match_percentage'] >= 95
            })

            if comparison['match_percentage'] >= 95:
                return True

        return False

    def compare_results(self, original, reproduction):
        differences = []
        matches = 0
        total_checks = 0

        # Comparar estado
        if original['status'] == reproduction['status']:
            matches += 1
        else:
            differences.append(f"Discrepancia de estado: {original['status']} vs {reproduction['status']}")
        total_checks += 1

        # Comparar pasos
        for orig_step, repro_step in zip(original['steps'], reproduction['steps']):
            if orig_step['status'] == repro_step['status']:
                matches += 1
            else:
                differences.append(
                    f"Discrepancia de estado del paso {orig_step['stepNumber']}: "
                    f"{orig_step['status']} vs {repro_step['status']}"
                )
            total_checks += 1

        match_percentage = (matches / total_checks) * 100

        return {
            "match_percentage": match_percentage,
            "differences": differences,
            "total_checks": total_checks,
            "matches": matches
        }

Almacenamiento y Gestión de Registros

Arquitectura de Almacenamiento

El almacenamiento eficiente de registros equilibra accesibilidad, retención y costo:

Niveles de Almacenamiento:

NivelRetenciónTipo de AlmacenamientoPatrón de Acceso
Caliente30 díasSSD / Base de datosAcceso frecuente, consultas rápidas
Templado90 díasAlmacenamiento de objetos (S3)Acceso ocasional
Frío1-7 añosAlmacenamiento de archivo (Glacier)Cumplimiento, acceso raro

Ejemplo de Implementación:

# Sistema de retención y archivo de registros
from datetime import datetime, timedelta
import boto3
import json

class ExecutionLogManager:
    def __init__(self):
        self.db = DatabaseConnection()
        self.s3 = boto3.client('s3')
        self.bucket = 'test-execution-logs'

    def store_execution_log(self, execution_log):
        # Almacenar en nivel caliente (base de datos) para acceso reciente
        self.db.insert('execution_logs', execution_log)

        # También respaldar en S3 para durabilidad
        s3_key = f"logs/{execution_log['executionId']}.json"
        self.s3.put_object(
            Bucket=self.bucket,
            Key=s3_key,
            Body=json.dumps(execution_log),
            StorageClass='STANDARD'
        )

    def archive_old_logs(self):
        # Mover registros con más de 30 días a nivel templado
        cutoff_date = datetime.now() - timedelta(days=30)
        old_logs = self.db.query(
            'SELECT * FROM execution_logs WHERE execution_time < %s',
            (cutoff_date,)
        )

        for log in old_logs:
            # Transición a STANDARD_IA (nivel templado)
            s3_key = f"logs/{log['executionId']}.json"
            self.s3.copy_object(
                Bucket=self.bucket,
                CopySource={'Bucket': self.bucket, 'Key': s3_key},
                Key=s3_key,
                StorageClass='STANDARD_IA'
            )

            # Eliminar de base de datos caliente
            self.db.delete('execution_logs', {'executionId': log['executionId']})

    def archive_compliance_logs(self):
        # Mover registros con más de 90 días a nivel frío (Glacier)
        cutoff_date = datetime.now() - timedelta(days=90)

        # Transición a GLACIER para retención a largo plazo
        lifecycle_policy = {
            'Rules': [{
                'Id': 'ArchiveOldLogs',
                'Status': 'Enabled',
                'Prefix': 'logs/',
                'Transitions': [{
                    'Days': 90,
                    'StorageClass': 'GLACIER'
                }],
                'Expiration': {
                    'Days': 2555  # 7 años para cumplimiento
                }
            }]
        }

        self.s3.put_bucket_lifecycle_configuration(
            Bucket=self.bucket,
            LifecycleConfiguration=lifecycle_policy
        )

Mejores Prácticas y Errores Comunes

Mejores Prácticas

  1. Estandarizar Formatos de Registro: Usar esquemas JSON o XML consistentes en todas las ejecuciones de prueba
  2. Automatizar Recopilación de Evidencias: La captura manual de capturas de pantalla es propensa a errores; automatizar siempre que sea posible
  3. Control de Versiones de Datos de Prueba: Almacenar scripts de configuración de datos de prueba en control de versiones
  4. Vincular Defectos Inmediatamente: Referenciar tickets de errores en registros de ejecución tan pronto como se encuentren problemas
  5. Incluir Métricas de Rendimiento: Siempre registrar duración de ejecución y uso de recursos del sistema
  6. Mantener Trazabilidad: Vincular registros de ejecución a casos de prueba, requisitos y sprints

Errores Comunes a Evitar

ErrorImpactoSolución
Faltan detalles del entornoNo se pueden reproducir fallasCaptura automatizada del entorno
Evidencia insuficienteDisputas de defectos, retrasos en depuraciónReglas obligatorias de captura/registro
Nomenclatura inconsistenteCaos en organización de evidenciasConvenciones estrictas de nomenclatura
Sin política de retención de registrosExplosión de costos de almacenamientoEstrategia de retención por niveles
Falta estado de datos de pruebaFallas falsasInstantánea/restauración de base de datos
Confusión de zonas horariasErrores relacionados con tiempoSiempre usar marcas de tiempo UTC

Conclusión

El registro completo de ejecución de pruebas no es solo documentación—es una inversión en calidad, eficiencia y colaboración de equipo. Los registros de ejecución bien mantenidos aceleran la depuración, permiten análisis de tendencias precisos, apoyan requisitos de cumplimiento y construyen conocimiento institucional que persiste más allá de los miembros individuales del equipo.

Al implementar prácticas de registro estructuradas, recopilación automatizada de evidencias y estrategias robustas de almacenamiento, los equipos de QA transforman las pruebas de una actividad transitoria en un activo valioso y permanente que mejora continuamente la calidad del software.