expect.soft() de Playwright exécute toutes les assertions d'un test avant d'échouer. Un test qui vérifie dix propriétés indépendantes d'une page signale ainsi tous les échecs à la fois, plutôt que de s'arrêter au premier.

Comment fonctionnent les assertions souples

import { test, expect } from '@playwright/test';

test('la page de profil contient tous les éléments requis', async ({ page }) => {
  await page.goto('/profile');

  // Assertions souples — toutes s'exécutent même si certaines échouent
  await expect.soft(page.getByLabel('Nom complet')).toBeVisible();
  await expect.soft(page.getByLabel('Email')).toBeVisible();
  await expect.soft(page.getByLabel('Numéro de téléphone')).toBeVisible();
  await expect.soft(page.getByRole('button', { name: 'Enregistrer les modifications' })).toBeEnabled();
  await expect.soft(page.getByRole('img', { name: 'Photo de profil' })).toBeVisible();

  // C'est ici que le test échoue réellement — si une assertion souple a échoué ci-dessus
  expect(test.info().errors).toHaveLength(0);
});

Chaque expect.soft() s'exécute. Les échecs sont collectés. Le test continue. À la fin (ou quand vous vérifiez test.info().errors), tous les échecs sont signalés en même temps.

Assertions dures vs assertions souples

Utilisez les assertions dures (expect) quand un échec rend le reste du test sans signification, quand vous assertez une précondition, ou quand un premier échec en implique logiquement d'autres.

Utilisez les assertions souples (expect.soft) pour vérifier plusieurs propriétés indépendantes d'une même page, quand chaque échec est une information de débogage utile en soi. C'est le bon choix pour vérifier qu'un formulaire, une ligne de tableau ou une carte contient tous ses champs.

test('la confirmation de commande affiche tous les détails', async ({ page }) => {
  await page.goto('/orders/12345');

  // Assertion dure — si ça échoue, rien d'autre n'a d'importance
  await expect(page.getByRole('heading', { name: 'Commande #12345' })).toBeVisible();

  // Assertions souples — vérifications indépendantes, toutes utiles
  await expect.soft(page.getByText('Statut : Confirmée')).toBeVisible();
  await expect.soft(page.getByText('Livraison : 2 à 5 jours ouvrés')).toBeVisible();
  await expect.soft(page.getByText('Total : 149,99 €')).toBeVisible();
  await expect.soft(page.getByRole('button', { name: 'Suivre la commande' })).toBeEnabled();
  await expect.soft(page.getByRole('button', { name: 'Annuler la commande' })).toBeEnabled();
});

Lire les échecs d'assertions souples

Quand une assertion souple échoue, Playwright signale tous les échecs ensemble :

Error: 2 soft assertion(s) failed.

  1) Soft assertion failed: expect(locator).toBeVisible()

    Call log:
      - waiting for page.getByText('Statut : Confirmée')

    Error: expect(locator).toBeVisible() with timeout 5000ms
    Received: hidden

  2) Soft assertion failed: expect(locator).toBeEnabled()

    Call log:
      - waiting for page.getByRole('button', { name: 'Annuler la commande' })

    Error: expect(locator).toBeEnabled() with timeout 5000ms
    Received: disabled

Deux problèmes en une exécution, pas un problème par exécution.

Assertions souples dans les tests de validation de formulaires

La validation de formulaire est un cas d'usage classique : beaucoup d'états d'erreur à vérifier, tous indépendants.

test('le formulaire affiche toutes les erreurs de validation à la soumission vide', async ({ page }) => {
  await page.goto('/register');
  await page.getByRole('button', { name: 'Créer un compte' }).click();

  // Vérifier que tous les messages de validation apparaissent
  await expect.soft(page.getByText('L\'email est requis')).toBeVisible();
  await expect.soft(page.getByText('Le mot de passe est requis')).toBeVisible();
  await expect.soft(page.getByText('Le prénom est requis')).toBeVisible();
  await expect.soft(page.getByText('Le nom est requis')).toBeVisible();

  // Vérifier que le bouton est toujours disponible après un échec de soumission
  await expect.soft(page.getByRole('button', { name: 'Créer un compte' })).toBeEnabled();
});

Sans assertions souples, vous ne connaîtriez que le premier message manquant et devriez relancer le test trois fois pour trouver les quatre problèmes.

Vérifier les erreurs explicitement

Vous pouvez vérifier les échecs des assertions souples à n'importe quel moment dans le test :

test('vérification de complétude de la page produit', async ({ page }) => {
  await page.goto('/products/laptop-pro');

  // Section 1 : section hero
  await expect.soft(page.getByRole('heading', { level: 1 })).toBeVisible();
  await expect.soft(page.getByRole('img', { name: /produit/i })).toBeVisible();
  await expect.soft(page.getByText(/\d+[,\.]\d{2}\s*€/)).toBeVisible(); // Prix

  // Si la section hero a des échecs, s'arrêter ici — inutile de vérifier le reste
  if (test.info().errors.length > 0) {
    test.fail();
    return;
  }

  // Section 2 : s'exécute uniquement si la section hero a passé
  await expect.soft(page.getByRole('button', { name: 'Ajouter au panier' })).toBeEnabled();
  await expect.soft(page.getByRole('tab', { name: 'Avis' })).toBeVisible();
  await expect.soft(page.getByRole('tab', { name: 'Spécifications' })).toBeVisible();
});

Mélanger assertions dures et souples

Le schéma le plus efficace : assertions dures pour les préconditions et l'état critique, assertions souples pour les propriétés individuelles.

test('le tableau de bord charge tous les widgets', async ({ page }) => {
  await page.goto('/dashboard');

  // Dur — si ça échoue, la page n'est pas chargée du tout
  await expect(page).toHaveTitle(/Tableau de bord/);
  await expect(page.getByRole('main')).toBeVisible();

  // Souple — chaque widget est indépendant
  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();

  // Souple — chaque métrique est indépendante
  await expect.soft(page.getByTestId('revenue-amount')).toContainText('€');
  await expect.soft(page.getByTestId('orders-count')).toContainText(/\d+/);
});

Quand ne pas utiliser les assertions souples

Les assertions souples collectent les échecs mais laissent le test continuer. Ça peut masquer des problèmes d'état :

// Dangereux — si le clic n'a pas fonctionné, les assertions ci-dessous testent un mauvais état
await expect.soft(page.getByRole('button', { name: 'Soumettre' })).toBeEnabled();
await page.getByRole('button', { name: 'Soumettre' }).click();
await expect.soft(page.getByText('Commande confirmée')).toBeVisible(); // peut passer accidentellement

Si des actions dépendent du succès d'assertions précédentes, utilisez des assertions dures. Les assertions souples servent à l'observation (vérifier ce qui est sur la page), pas au contrôle du flux.

→ See also: Assertions dans Playwright: Le Guide Complet | Structure des Tests Playwright: describe, beforeEach, afterEach et Hooks | Comment Lire les Messages d'Erreur de Playwright