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 Registro | Propósito | Método de Recopilación |
---|---|---|
Consola del Navegador | Errores JavaScript, advertencias | driver.get_log('browser') |
Tráfico de Red | Llamadas API, tiempos de respuesta | Exportación de archivo HAR |
Registros de Aplicación | Errores backend, trazas de pila | Herramientas de agregación de registros |
Consultas de Base de Datos | Operaciones de datos, rendimiento | Registro de consultas |
Registros del Servidor | Problemas de infraestructura | Registro 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:
Componente | Entorno de Desarrollo | Entorno de Staging | Entorno de Producción |
---|---|---|---|
Versión de Aplicación | v2.4.1-dev | v2.4.1-RC3 | v2.4.0 |
Versión de Base de Datos | PostgreSQL 15.3 | PostgreSQL 15.3 | PostgreSQL 15.2 |
SO | Ubuntu 22.04 | Ubuntu 22.04 | Ubuntu 20.04 |
Node.js | v20.10.0 | v20.10.0 | v18.18.0 |
Redis | 7.2.0 | 7.2.0 | 7.0.11 |
Balanceador de Carga | Ninguno | Nginx 1.24 | Nginx 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:
- Scripts de configuración de datos de prueba
- Procedimientos de poblamiento de base de datos
- Estados de archivos de configuración
- Configuraciones simuladas de servicios de terceros
- 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
- Navegar a https://staging.app.com/login
- Verificar que el formulario de inicio de sesión muestre campos de email y contraseña
- Ingresar email: testuser@example.com
- Ingresar contraseña: [del vault]
- Hacer clic en botón “Iniciar Sesión”
- Verificar redirección al panel en 2 segundos
- 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:
Nivel | Retención | Tipo de Almacenamiento | Patrón de Acceso |
---|---|---|---|
Caliente | 30 días | SSD / Base de datos | Acceso frecuente, consultas rápidas |
Templado | 90 días | Almacenamiento de objetos (S3) | Acceso ocasional |
Frío | 1-7 años | Almacenamiento 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
- Estandarizar Formatos de Registro: Usar esquemas JSON o XML consistentes en todas las ejecuciones de prueba
- Automatizar Recopilación de Evidencias: La captura manual de capturas de pantalla es propensa a errores; automatizar siempre que sea posible
- Control de Versiones de Datos de Prueba: Almacenar scripts de configuración de datos de prueba en control de versiones
- Vincular Defectos Inmediatamente: Referenciar tickets de errores en registros de ejecución tan pronto como se encuentren problemas
- Incluir Métricas de Rendimiento: Siempre registrar duración de ejecución y uso de recursos del sistema
- Mantener Trazabilidad: Vincular registros de ejecución a casos de prueba, requisitos y sprints
Errores Comunes a Evitar
Error | Impacto | Solución |
---|---|---|
Faltan detalles del entorno | No se pueden reproducir fallas | Captura automatizada del entorno |
Evidencia insuficiente | Disputas de defectos, retrasos en depuración | Reglas obligatorias de captura/registro |
Nomenclatura inconsistente | Caos en organización de evidencias | Convenciones estrictas de nomenclatura |
Sin política de retención de registros | Explosión de costos de almacenamiento | Estrategia de retención por niveles |
Falta estado de datos de prueba | Fallas falsas | Instantánea/restauración de base de datos |
Confusión de zonas horarias | Errores relacionados con tiempo | Siempre 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.