Sviluppo web
Come usare Playwright: dal primo test ai fixture
Playwright regge una suite grande con tre pezzi: progetti, un fixture per il login e `storageState` rigenerato. Comandi e costi reali.
In sintesi
- Il progetto `setup` autentica una volta e `dependencies` rende l'ordine esplicito.
- `test.extend` sostituisce `beforeAll` senza condividere stato fra file.
- `--last-failed` e `--shard` rendono la suite sopportabile, ma lo stato salvato scade con la sessione.
Playwright si usa in tre passi: un file di configurazione con i progetti, un fixture che prepara la pagina e un comando per eseguire la suite. La versione misurata è @playwright/test 1.64.1.
Configurazione con progetti
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./e2e",
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 2 : undefined,
reporter: [["html", { open: "never" }], ["github"]],
use: { baseURL: "http://localhost:5173", trace: "on-first-retry" },
projects: [
{ name: "setup", testMatch: /auth\.setup\.ts/ },
{
name: "chromium",
use: { ...devices["Desktop Chrome"], storageState: "e2e/.auth/user.json" },
dependencies: ["setup"],
},
],
});
Il progetto setup esegue solo auth.setup.ts e salva lo stato di sessione; il progetto chromium dipende da setup e riusa quello stato. dependencies rende l'ordine esplicito, invece di affidarlo al nome dei file.
Il login una volta sola
import { test as setup, expect } from "@playwright/test";
setup("autentica e salva lo stato", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("[email protected]");
await page.getByLabel("Password").fill("demo1234");
await page.getByRole("button", { name: "Accedi" }).click();
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
await page.context().storageState({ path: "e2e/.auth/user.json" });
});
storageState scrive cookie e localStorage su disco. Il file è una credenziale: va in .gitignore, e in CI si rigenera a ogni esecuzione.
Fixture: setup condiviso senza beforeAll
import { test as base, expect } from "@playwright/test";
import { LoginPage } from "./pages/login";
type Fixtures = { loginPage: LoginPage };
export const test = base.extend({
loginPage: async ({ page }, use) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await use(loginPage);
},
});
export { expect };
test.extend crea un fixture tipizzato, lo esegue una volta per test e lo chiude quando il test finisce. Sostituisce beforeAll con lo stesso costo, ma senza stato condiviso fra file.
Comandi che usi ogni giorno
npx playwright test --project=chromium
npx playwright test --last-failed
npx playwright test --shard=1/4
npx playwright show-report
--last-failed riesegue solo i test falliti nell'ultima run; --shard=1/4 divide la suite in quattro parti per il CI. show-report apre l'HTML con passi, trace e screenshot.
Debug di un test rosso
npx playwright test --ui
npx playwright test --debug
npx playwright test -g "login" --repeat-each=10
L'interfaccia --ui fa avanzare il test passo per passo; --repeat-each=10 ripete lo stesso test dieci volte e fa emergere le instabilità che una singola run nasconde.
Limite dichiarato
Lo stato salvato scade quando scade la sessione dell'app: se il cookie dura un'ora, la suite notturna parte con uno stato vecchio e il primo test fallisce senza un bug. Rigenera lo stato a ogni run invece di committarlo, e ricorda che con lo sharding ogni parte rifà il login.
Dove continuare
Tre componenti da coprire con un percorso: login-form, otp-input e input; per sfogliare per categoria parti da components/lib e dalle 1.556 anteprime. L'installazione di base è in Playwright: setup e primi test; i test instabili in come trovare un flaky test. Il confronto fra framework è in Vue 3 vs React.
Domande frequenti
Devo autenticarmi in ogni test?
No. Un progetto setup esegue il login una volta e salva storageState; gli altri progetti lo riusano. Il costo è che lo stato scade insieme alla sessione, quindi in CI va rigenerato.
Che differenza c'è fra beforeAll e un fixture?
beforeAll gira una volta per file e condivide lo stato fra i test. Un fixture gira una volta per test e viene chiuso alla fine, quindi non lascia residui.
Posso dividere la suite su più macchine?
Sì, con --shard=1/4 e valori diversi per ogni macchina. Ogni parte rifà il setup del login, quindi il risparmio cresce con la durata dei test, non con il loro numero.
Dove finisce la trace dei test falliti?
In test-results, dentro la cartella del test. Si apre con show-report oppure con show-trace e contiene passi, rete e snapshot DOM.
Serve un server già acceso per lanciare i test?
No, se configuri webServer nel file di configurazione: Playwright avvia il comando, attende la porta e lo spegne alla fine. Con reuseExistingServer riusa quello che hai già aperto in locale.