Home > Blog > Playwright: Efficient Verification with Soft Assertions
Author: Tomotaka ASAGI
Published: Jan 31, 2026
Introduction
After years of working with E2E automation, you inevitably encounter all sorts of testing scenarios. The most frequent request is usually the same: "Automate these manual tests."
In the early days, I used to automate these tests without much forethought, and things often went sideways. For instance, a single manual test case often intertwines operations and checkpoints, requiring dozens of validations. Whether you view this as one large test or multiple smaller ones, the automation ends up as a repetitive sequence of "step → check → step → check..."
This raises a critical question: When a single check fails, should we skip the rest? Should the entire test case be marked as failed immediately?
When testing manually, you can visually inspect a screen and judge multiple items at once—"this passes, that fails." Automated testing, however, doesn't inherently work that way; usually, the script stops the moment it hits an error.
Nowadays, we'd typically handle fine-grained checks in unit tests following the "one test, one assertion" principle. But applying that same logic to E2E tests is a different story. If you have 100 checkpoints on a single screen, you don't want to log in and navigate there 100 separate times just to follow a principle. That's a massive waste of time and resources.
This is where you need a framework that supports "soft assertions"—the ability to continue testing even after a validation fails. I used to build these custom functions myself, but Playwright now has this capability built-in.
Soft Assertions: Verification That Continues After Failure
The Difference Between Hard and Soft Assertions
Hard Assertion (regular expect)
await expect(page.getByTestId('name')).toHaveText('John Smith');
// ↑ Test stops immediately on failure
await expect(page.getByTestId('email')).toHaveText('john@example.com');
// ↑ This won't run if the above fails
Soft Assertion (expect.soft)
await expect.soft(page.getByTestId('name')).toHaveText('John Smith');
// ↑ Test continues even on failure
await expect.soft(page.getByTestId('email')).toHaveText('john@example.com');
// ↑ This runs even if the above fails
Benefits of Soft Assertions
When verifying multiple items on a read-only screen, Soft Assertions let you identify all issues in a single test run.
With traditional Hard Assertions:
- First one fails → fix → re-run
- Second one fails → fix → re-run
- ...repeat
With Soft Assertions:
- One run shows all 6 results
- Fix everything at once → re-run
Guidelines for Choosing
Scenario | Recommendation |
Login success verification | Hard Assertion |
Page navigation verification | Hard Assertion |
Multiple field verification on read-only screens | Soft Assertions |
Form validation verification | Soft Assertions |
Tests where previous steps must succeed | Hard Assertion |
Early Exit with Checkpoint
If you want to use Soft Assertions but stop at certain points when errors occur:
Using expect.configure for Default Soft Behavior
When writing many Soft Assertions, expect.configure makes your code cleaner.
Adding Custom Messages
You can add custom messages to make failure reports clearer.
await expect.soft(
page.getByTestId('customer-name'),
'Customer name should be displayed correctly'
).toHaveText('John Smith');
Important Constraint
Soft assertions only work with Playwright test runner.
This is one of the "TypeScript-only features" I mentioned in Part 2. It's not available in the Java version of Playwright.
Using Soft Assertions with Playwright-bdd
Designing Then Steps with Soft Assertions
In BDD, traditionally one Then step represents one verification. However, with Soft Assertions, you have the option to combine multiple related verifications into a single Then step.
Option 1: Traditional 1 Then = 1 Assertion
Scenario: Display customer details
Given the user is logged in
And the customer detail screen is open
Then the customer name "John Smith" is displayed
And the customer email "john@example.com" is displayed
And the customer phone "555-1234" is displayed
Option 2: Combine with Soft Assertions
Scenario: Display customer details
Given the user is logged in
And the customer detail screen is open
Then the customer basic information is displayed correctly
Then('the customer basic information is displayed correctly', async ({ page }) => {
await expect.soft(page.getByTestId('customer-name')).toHaveText('John Smith');
await expect.soft(page.getByTestId('customer-email')).toHaveText('john@example.com');
await expect.soft(page.getByTestId('customer-phone')).toHaveText('555-1234');
});
Option 3: Use Scenario Outline with Examples
When you have many verification items or need to repeat similar verifications, Scenario Outline with Examples is also effective.
Scenario Outline vs Soft Assertions
Aspect | Scenario Outline + Examples | Soft Assertions |
Test case handling | Each row is an independent test case | Multiple verifications in one test case |
Behavior on failure | Other rows still run if one fails | Other verifications still run if one fails |
Report display | Pass/Fail shown per row | Multiple failures shown in one test |
Feature file | Target and expected values clearly visible | Verification details abstracted |
Which to Choose
Option | Pros | Cons |
1 Then = 1 Assertion | Feature file is detailed and readable | Many steps, stops on first failure |
Combined Soft Assertions | Efficient, see all issues at once | Feature file becomes abstract |
Scenario Outline + Examples | Data in table format, rows are independent | Requires mapping logic in step definitions |
My approach for read-only screen verification:
- Few items (3-5): Combine with Soft Assertions
- Many items, or want table-based management: Scenario Outline + Examples
- Business logic verification: Individual Then steps
Revisiting "One Test, One Assertion"
The "One Test, One Assertion" approach I mentioned at the beginning made sense in a world with only Hard Assertions.
However, with Soft Assertions, it's better to think of this as having more options.
Approach | When to Apply |
One Test, One Assertion | State transition tests, critical flows |
One Test, Multiple Assertions (Soft) | Read-only screens, form validation |
It's not about which is "correct" — it's about choosing based on the situation.
Summary
Feature | Previous Challenge | Playwright Solution |
Soft Assertions | Everything stops on first failure, required custom implementation | Provided as standard feature |
By combining storageState + Projects from the previous article with Soft Assertions from this article, you can write efficient tests for authenticated read-only screens.
References
- Playwright Test Assertions
- Playwright-bdd Documentation
This article is Part 5 of the "Playwright Series."
- Part 1: From Selenium to Playwright
- Part 2: TypeScript vs Java - Feature Differences by Language
- Part 3: BDD Framework Comparison - Cucumber.js vs Playwright-bdd
- Part 4: Streamlining Authentication with storageState
- Part 5: Efficient Verification with Soft Assertions (this article)