Salta al contenuto principale

Playwright Component Testing: setup completo

Articolo della serie Playwright Component Testing Avanzato. Setup completo di Playwright per il component testing in Angular con fixture, configuration e best practice.

Cosa ne pensi di questo articolo?

Playwright Component Testing: setup completo
Playwright ComponentsFoto:Ruben BoekelooPexels

In Breve

Articolo della serie Playwright Component Testing Avanzato. Setup completo di Playwright per il component testing in Angular con fixture, configuration e best practice.

Playwright Component Testing: perché e come iniziare

Playwright per il component testing rappresenta uno dei modi più efficienti per validare componenti singoli in isolamento, prima ancora di integrarli nell'applicazione completa. A differenza dei test di integrazione o end-to-end, il component testing si focalizza su un singolo componente UI, interagisce con esso direttamente e verifica che il comportamento sia corretto. Questo articolo fornisce un setup completo per iniziare subito.

💡 Cosa imparerai

  • Come installare e configurare Playwright per il component testing
  • Setup del file playwright.config.ts
  • Scrivere il primo test di componente
  • Utilizzo di fixture e helper per semplificare i test
  • Best practice per component testing su larga scala

Differenza tra Component Testing e E2E Testing

Component testing valida un singolo componente in isolamento. Il componente è renderizzato in una pagina di test dedicata, e si interagisce direttamente via DOM senza l'intera applicazione dietro. Questo rende i test più veloce, deterministici e facili da debuggare.

E2E testing, al contrario, avvia l'applicazione completa e simula il percorso reale dell'utente attraverso intere pagine. E2E è più "realista" ma anche più lento e fragile.

Prerequisiti

Per seguire questo tutorial hai bisogno di:

  • Node.js 16+ e npm/yarn
  • Un progetto Angular (oppure React/Vue, Playwright supporta tutti)
  • Familiarità di base con TypeScript e il testing
  • Editor di codice (VS Code consigliato)

Installazione di Playwright CT (Component Testing)

Playwright mette a disposizione un package dedicato al component testing: @playwright/experimental-ct-angular (o per React/Vue). Installa via npm:

Bash - Installazione Playwright CT
npm install --save-dev @playwright/experimental-ct-angular @playwright/test

Questo installerà il test runner, le utilità di mounting componenti, e tutti gli strumenti necessari. Non c'è bisogno di browserify, webpack, o ulteriori configurazioni — Playwright gestisce tutto internamente.

Configurazione del File playwright.config.ts

Crea (o aggiorna) il file playwright.config.ts nella root del progetto:

TypeScript - playwright.config.ts
import {{ '{' }} defineConfig, devices {{ '}' }} from '@playwright/experimental-ct-angular';

export default defineConfig({{ '{' }}
  testDir: 'src/**/*.spec.ts',
  testMatch: '**/*.spec.ts',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',
  use: {{ '{' }}
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  {{ '}' }},
  webServer: {{ '{' }}
    command: 'npm run ng serve',
    port: 4200,
    reuseExistingServer: !process.env.CI,
  {{ '}' }},
  projects: [
    {{ '{' }}
      name: 'chromium',
      use: {{ '{' }} ...devices['chromium'] {{ '}' }},
    {{ '}' }},
    {{ '{' }}
      name: 'firefox',
      use: {{ '{' }} ...devices['firefox'] {{ '}' }},
    {{ '}' }},
    {{ '{' }}
      name: 'webkit',
      use: {{ '{' }} ...devices['webkit'] {{ '}' }},
    {{ '}' }},
  ],
{{ '}' }});

La configurazione abilita:

  • testDir: Individua tutti i test nelle cartelle src
  • fullyParallel: Esegue i test in parallelo per velocità
  • reporter: Genera report HTML interattivi
  • trace e screenshot: Registra tracce e screenshot su fallimento
  • projects: Testa su Chromium, Firefox, WebKit

Scrivere il Primo Test di Componente

Crea un componente di esempio (ad esempio src/app/button.component.ts):

TypeScript - button.component.ts
import {{ '{' }} Component, Input, Output, EventEmitter {{ '}' }} from '@angular/core';

@Component({{ '{' }}
  selector: 'app-button',
  standalone: true,
  template: `
    <button
      (click)="onClick()"
      [disabled]="isDisabled"
      class="btn"
    >
      {{ '{{' }} label {{ '}}' }}
    </button>
  `,
  styles: [`
    .btn {{ '{' }}
      padding: 8px 16px;
      cursor: pointer;
      background: #007bff;
      color: white;
      border: none;
      border-radius: 4px;
    {{ '}' }}
    .btn:disabled {{ '{' }}
      opacity: 0.5;
      cursor: not-allowed;
    {{ '}' }}
  `],
{{ '}' }})
export class ButtonComponent {{ '{' }}
  @Input() label: string = 'Click me';
  @Input() isDisabled: boolean = false;
  @Output() clicked = new EventEmitter<void>();

  onClick() {{ '{' }}
    this.clicked.emit();
  {{ '}' }}
{{ '}' }}

Ora crea il test accanto (stessa cartella):

TypeScript - button.component.spec.ts
import {{ '{' }} test, expect {{ '}' }} from '@playwright/experimental-ct-angular';
import {{ '{' }} ButtonComponent {{ '}' }} from './button.component';

test.describe('ButtonComponent', () => {{ '{' }}
  test('rendezza il label correttamente', async ({{ '{' }} mount {{ '}' }}) => {{ '{' }}
    const component = await mount(ButtonComponent, {{ '{' }}
      props: {{ '{' }} label: 'Submit' {{ '}' }},
    {{ '}' }});

    await expect(component).toContainText('Submit');
  {{ '}' }});

  test('emette evento al click', async ({{ '{' }} mount {{ '}' }}) => {{ '{' }}
    const component = await mount(ButtonComponent, {{ '{' }}
      props: {{ '{' }} label: 'Click' {{ '}' }},
    {{ '}' }});

    let eventCount = 0;
    component.on('clicked', () => {{ '{' }} eventCount++; {{ '}' }});

    await component.locator('button').click();
    expect(eventCount).toBe(1);
  {{ '}' }});

  test('disabilita il pulsante quando isDisabled è true', async ({{ '{' }} mount {{ '}' }}) => {{ '{' }}
    const component = await mount(ButtonComponent, {{ '{' }}
      props: {{ '{' }} isDisabled: true {{ '}' }},
    {{ '}' }});

    const button = component.locator('button');
    await expect(button).toBeDisabled();
  {{ '}' }});
{{ '}' }});

Esegui i test con:

Bash - Esecuzione test
npm run test:ct
# o
npx playwright test

Fixture Personalizzate e Helper

Per ridurre la duplicazione di codice, crea fixture personalizzate. Ad esempio, un file src/fixtures.ts:

TypeScript - src/fixtures.ts
import {{ '{' }} test as base {{ '}' }} from '@playwright/experimental-ct-angular';

export const test = base.extend({{ '{' }}
  customMount: async ({{ '{' }} mount, page {{ '}' }}, use) => {{ '{' }}
    // Helper per verificare accessibilità
    const mountWithA11y = async (component: any, options = {{ '}' }}) => {{ '{' }}
      const mounted = await mount(component, options);

      // Controlla ARIA attributes
      const axeResults = await page.evaluate(() => {{ '{' }}
        return (window as any).axe?.run();
      {{ '}' }});

      return mounted;
    {{ '}' }};

    await use(mountWithA11y);
  {{ '}' }},
{{ '}' }});

export const expect = base.expect;

Best Practice per Component Testing su Scala

Quando i test crescono, seguire queste best practice evita problemi:

1. Organizzazione Modulare

Mantieni i test vicino ai componenti:

src/
  ├── app/
  │   ├── button/
  │   │   ├── button.component.ts
  │   │   └── button.component.spec.ts
  │   ├── form/
  │   │   ├── form.component.ts
  │   │   └── form.component.spec.ts

2. Usa Data Attribute per Selezione

Evita di legare i test a classi CSS che cambiano frequentemente. Usa data-testid:

// Template
<button data-testid="submit-btn">Submit</button>

// Test
const submitBtn = component.locator('[data-testid="submit-btn"]');
await submitBtn.click();

3. Test l'Interfaccia, Non l'Implementazione

Testa i comportamenti pubblici e i risultati visibili, non i dettagli interni.

4. Mantieni i Test Isolati

Ogni test deve essere indipendente. Non fare affidamento su stato di test precedenti.

Aggiunta ai Script di package.json

Aggiungi questi script al tuo package.json per facilitare l'esecuzione:

JSON - package.json scripts
"test:ct": "playwright test --config=playwright.config.ts",
"test:ct:ui": "playwright test --config=playwright.config.ts --ui",
"test:ct:debug": "playwright test --config=playwright.config.ts --debug",
"test:ct:report": "playwright show-report"

Integrazione con CI/CD

Per integrare in GitHub Actions:

YAML - .github/workflows/test.yml
name: Playwright Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm install
      - run: npm run test:ct

Troubleshooting Comune

Problema: "mount non è definito"
Soluzione: Assicurati di importare test e expect da @playwright/experimental-ct-angular, non da @playwright/test direttamente.

Problema: Componente non renderizza
Soluzione: Accertati che il server dev sia attivo. Verifica la porta nel webServer config.

Problema: Test flaky (intermittente)
Soluzione: Usa waitFor espliciti e avoid timeouts troppo brevi. Disabilita esecuzione parallela se ci sono effetti collaterali.

FAQ

È necessario usare mock in component testing?

No sempre. Se il componente dipende da servizi, puoi passare mock via dependency injection. Playwright CT supporta direttamente la configurazione di provider Angular.

Posso testare componenti con Reactive Forms?

Sì. Testa form come faresti normalmente. Playwright supporta completamente Reactive Forms e tutte le validazioni.

Quale è il tempo medio per eseguire suite di component test?

Dipende dal numero di test e dalla parallelizzazione. Tipicamente 100-200 component test girano in 30-60 secondi su hardware moderno.

Posso usare Playwright CT per e2e test?

Sì, ma non è il suo scopo primario. Per e2e test completi, usa @playwright/test standard che fornisce più controllo sul browser.

Come incorporo Playwright CT nel mio CI/CD?

Aggiungi i comandi npm run test:ct nei tuoi workflow GitHub Actions, GitLab CI, o altro. Assicurati che il server dev sia disponibile durante l'esecuzione.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti