Un expect() estándar detiene el test en el primer fallo. Un formulario con cuatro mensajes de validación rotos requiere cuatro ejecuciones de test para diagnosticarlo por completo. expect.soft() recopila todos los fallos y los reporta juntos al final, pero el test igual falla si alguna aserción soft falló: continúa ejecutándose, no aprobándose. Este artículo cubre la API, los criterios para elegir entre aserciones hard vs. soft, cómo inspeccionar test.info().errors para detener antes cuando falla una precondición, y el patrón que combina aserciones hard para el estado crítico de la página con aserciones soft para propiedades independientes.
Cómo funcionan las aserciones soft
import { test, expect } from '@playwright/test';
test('la página de perfil tiene todos los elementos requeridos', async ({ page }) => {
await page.goto('/profile');
// Aserciones soft — todas se ejecutan aunque algunas fallen
await expect.soft(page.getByLabel('Full name')).toBeVisible();
await expect.soft(page.getByLabel('Email')).toBeVisible();
await expect.soft(page.getByLabel('Phone number')).toBeVisible();
await expect.soft(page.getByRole('button', { name: 'Save changes' })).toBeEnabled();
await expect.soft(page.getByRole('img', { name: 'Profile photo' })).toBeVisible();
// Aquí es donde el test realmente falla — si alguna aserción soft falló arriba
expect(test.info().errors).toHaveLength(0);
});Cada expect.soft() se ejecuta. Los fallos se acumulan. El test continúa. Al final (o cuando verificas test.info().errors), todos los fallos se reportan juntos.
Aserciones hard vs. soft
Usa aserciones hard (expect) cuando un fallo hace que el resto del test no tenga sentido (por ejemplo, el login falló, nada más funciona), cuando estás verificando una precondición en lugar de un resultado, o cuando un fallo implica lógicamente los demás (la navegación falló, nada en la página importa).
Usa aserciones soft (expect.soft) cuando estás verificando múltiples propiedades independientes de la misma página, cuando cada fallo es información de depuración independientemente útil, o cuando estás verificando que un formulario, fila de tabla o tarjeta tiene todos sus campos.
test('la confirmación de orden muestra todos los detalles', async ({ page }) => {
await page.goto('/orders/12345');
// Aserción hard — si esto falla, nada más importa
await expect(page.getByRole('heading', { name: 'Order #12345' })).toBeVisible();
// Aserciones soft — verificaciones independientes, todas útiles
await expect.soft(page.getByText('Status: Confirmed')).toBeVisible();
await expect.soft(page.getByText('Delivery: 2–5 business days')).toBeVisible();
await expect.soft(page.getByText('Total: $149.99')).toBeVisible();
await expect.soft(page.getByRole('button', { name: 'Track order' })).toBeEnabled();
await expect.soft(page.getByRole('button', { name: 'Cancel order' })).toBeEnabled();
});Leer los fallos de aserciones soft
Cuando una aserción soft falla, Playwright reporta todos los fallos juntos:
Error: 2 soft assertion(s) failed.
1) Soft assertion failed: expect(locator).toBeVisible()
Call log:
- waiting for page.getByText('Status: Confirmed')
Error: expect(locator).toBeVisible() with timeout 5000ms
Received: hidden
2) Soft assertion failed: expect(locator).toBeEnabled()
Call log:
- waiting for page.getByRole('button', { name: 'Cancel order' })
Error: expect(locator).toBeEnabled() with timeout 5000ms
Received: disabledDos problemas en una sola ejecución, no un problema por ejecución.
Aserciones soft en tests de validación de formularios
La validación de formularios es un caso de uso clásico: muchos estados de error que verificar, todos independientes.
test('el formulario muestra todos los errores de validación al enviar vacío', async ({ page }) => {
await page.goto('/register');
await page.getByRole('button', { name: 'Create account' }).click();
// Verificar que aparecen todos los mensajes de validación
await expect.soft(page.getByText('Email is required')).toBeVisible();
await expect.soft(page.getByText('Password is required')).toBeVisible();
await expect.soft(page.getByText('First name is required')).toBeVisible();
await expect.soft(page.getByText('Last name is required')).toBeVisible();
// Verificar que el botón de envío sigue disponible (no deshabilitado después del envío fallido)
await expect.soft(page.getByRole('button', { name: 'Create account' })).toBeEnabled();
});Sin aserciones soft, sabrías del primer mensaje faltante y tendrías que ejecutar el test tres veces más para encontrar los cuatro problemas.
Verificar errores explícitamente
Puedes verificar los fallos de aserciones soft en cualquier punto del test:
test('verificación de completitud de la página de producto', async ({ page }) => {
await page.goto('/products/laptop-pro');
// Sección 1: Hero section
await expect.soft(page.getByRole('heading', { level: 1 })).toBeVisible();
await expect.soft(page.getByRole('img', { name: /product/i })).toBeVisible();
await expect.soft(page.getByText(/\$\d+\.\d{2}/)).toBeVisible(); // Precio
// Si la hero section tiene fallos, parar aquí — no tiene sentido verificar el resto
if (test.info().errors.length > 0) {
test.fail();
return;
}
// Sección 2: Solo ejecutar si la hero pasó
await expect.soft(page.getByRole('button', { name: 'Add to cart' })).toBeEnabled();
await expect.soft(page.getByRole('tab', { name: 'Reviews' })).toBeVisible();
await expect.soft(page.getByRole('tab', { name: 'Specifications' })).toBeVisible();
});Combinar aserciones hard y soft
El patrón más efectivo: aserciones hard para precondiciones y estado crítico, aserciones soft para propiedades individuales:
test('el dashboard carga todos los widgets', async ({ page }) => {
await page.goto('/dashboard');
// Hard — si esto falla, la página no cargó en absoluto
await expect(page).toHaveTitle(/Dashboard/);
await expect(page.getByRole('main')).toBeVisible();
// Soft — cada widget es independiente
await expect.soft(page.getByTestId('revenue-widget')).toBeVisible();
await expect.soft(page.getByTestId('orders-widget')).toBeVisible();
await expect.soft(page.getByTestId('users-widget')).toBeVisible();
await expect.soft(page.getByTestId('activity-feed')).toBeVisible();
// Soft — cada métrica es independiente
await expect.soft(page.getByTestId('revenue-amount')).toContainText('$');
await expect.soft(page.getByTestId('orders-count')).toContainText(/\d+/);
});Cuándo no usar aserciones soft
Las aserciones soft acumulan fallos pero dejan continuar el test. Esto puede enmascarar problemas de estado:
// Peligroso — si el clic no funcionó, las aserciones de abajo testean el estado incorrecto
await expect.soft(page.getByRole('button', { name: 'Submit' })).toBeEnabled();
await page.getByRole('button', { name: 'Submit' }).click();
await expect.soft(page.getByText('Order confirmed')).toBeVisible(); // puede pasar accidentalmenteSi las acciones dependen de que pasen aserciones anteriores, usa aserciones hard. Las aserciones soft son para observación (verificar qué hay en la página), no para control de flujo.
→ See also: Assertions en Playwright: La Guía Completa | Estructura de Tests en Playwright: describe, beforeEach, afterEach y Hooks | Cómo Leer los Mensajes de Error de Playwright