Answer
Soft Assertions in Playwright
The Problem Soft Assertions Solve
With regular assertions, the test stops immediately on the first failure:
TypeScript
test('verify dashboard widgets', async ({ page }) => {
await page.goto('/dashboard');
await expect(page.locator('.widget-revenue')).toBeVisible(); // FAILS — stops here
await expect(page.locator('.widget-users')).toBeVisible(); // Never runs
await expect(page.locator('.widget-orders')).toBeVisible(); // Never runs
await expect(page.locator('.widget-conversion')).toBeVisible(); // Never runs
});
// Only 1 failure reported — you don't know about the other 3
Soft Assertions — Continue on Failure
TypeScript
test('verify all dashboard widgets', async ({ page }) => {
await page.goto('/dashboard');
// All of these run even if earlier ones fail
await expect.soft(page.locator('.widget-revenue')).toBeVisible();
await expect.soft(page.locator('.widget-users')).toBeVisible();
await expect.soft(page.locator('.widget-orders')).toBeVisible();
await expect.soft(page.locator('.widget-conversion')).toBeVisible();
await expect.soft(page.locator('.widget-traffic')).toBeVisible();
// All failures collected and reported at end of test
});
// Reports: 3 soft assertions failed — widget-users, widget-orders, widget-traffic missing
Mixing Soft and Hard Assertions
TypeScript
test('product page verification', async ({ page }) => {
await page.goto('/products/123');
// Hard — test won't continue if product page doesn't load
await expect(page).toHaveURL('/products/123');
// Soft — collect all UI failures
await expect.soft(page.locator('.product-title')).toBeVisible();
await expect.soft(page.locator('.product-price')).toContainText('$');
await expect.soft(page.locator('.product-image img')).toBeVisible();
await expect.soft(page.locator('.add-to-cart-btn')).toBeEnabled();
await expect.soft(page.locator('.product-reviews')).toBeVisible();
// Hard check at the end — ensure no soft failures before continuing
expect(test.info().errors).toHaveLength(0); // Fails if any soft assertion failed
});
Use testInfo.errors to Check Soft Failures
TypeScript
test('form field validation', async ({ page }, testInfo) => {
await page.goto('/register');
await page.getByRole('button', { name: 'Submit' }).click();
await expect.soft(page.getByText('Email is required')).toBeVisible();
await expect.soft(page.getByText('Password is required')).toBeVisible();
await expect.soft(page.getByText('Name is required')).toBeVisible();
// After collecting all failures
if (testInfo.errors.length > 0) {
console.log(`${testInfo.errors.length} validation messages missing`);
}
});
When to Use Soft vs Hard
| Situation | Use |
|---|---|
| Critical pre-condition (must pass to continue) | Hard expect() |
| Checking multiple UI elements on a page | Soft expect.soft() |
| Smoke test across many page elements | Soft expect.soft() |
| Business flow (login → checkout → confirm) | Hard expect() |
| Visual verification of multiple items | Soft expect.soft() |
