Coding

Nei DevTools filava tutto liscio… poi non più

I viewport simulati non intercettano i veri problemi di UX e compatibilità con l'OS. Ecco come eseguiamo i test Playwright su dispositivi Android e iOS reali con BrowserStack.

Andrea Gorletta Andrea Gorletta
20 ottobre 2025 — 10 min

Succede ai migliori: ci metti anima e cuore a costruire e testare una web application, ti crogioli nel verde dei test che passano dopo un attento ciclo di Test Driven Development (TDD), fai deploy e mostri orgoglioso il tuo nuovo e-commerce lucidissimo a tua zia sul suo vecchio iPhone X… per poi vedere il layout cadere a pezzi.

Il real-device testing ha il dono di far emergere i bug più sottili e inaspettati. In un progetto recente, un’applicazione Next.js estesa per un grande cliente, volevamo aumentare l’affidabilità dei nostri component test Playwright integrando il cloud di real-device testing di BrowserStack. Il processo ci ha regalato gioie e dolori in parti uguali, e abbiamo pensato che valesse la pena condividere ciò che abbiamo imparato.

I vantaggi del real-device testing

È noto che oggi la maggior parte degli utenti arriva sui siti da mobile, usando device e browser di ogni tipo. Affidarsi ai soli viewport simulati può facilmente nascondere problemi come:

  • piccoli mismatch tra l’OS mobile e l’engine del browser
  • supporto totalmente mancante o parziale per le browser API che la tua app usa o da cui dipende il tuo codice
  • quirk specifici del device ed edge case imprevedibili

Qualcuno potrebbe sostenere che questo livello di affidabilità sia solo un “nice-to-have”, visto che raggiungerlo richiede un impegno significativo da parte dei developer.

__wf_reserved_inherit

In realtà, però, quello stesso effort (se non di più) finirà comunque per essere speso a sistemare esattamente gli stessi problemi quando emergeranno durante lo User Acceptance Testing (UAT) o, peggio ancora, dopo il rilascio. A quel punto, chiaramente, il processo diventa più pesante: un utente segnala un bug, viene creato e assegnato un ticket, il problema viene riprodotto, fixato, testato di nuovo e infine patchato nell’app.

Per questo, secondo me, ne vale la pena per tutte le persone coinvolte. Tua zia dolcissima inclusa.

Ancora meglio, non serve reinventare la ruota: puoi riutilizzare i component test esistenti ed eseguirli direttamente su device reali, con quasi zero effort aggiuntivo rispetto a eseguirli in un ambiente simulato o emulato.

Come funziona BrowserStack sotto il cofano

In poche parole, BrowserStack è una delle poche piattaforme cloud che mantiene una device farm remota. Il servizio non è proprio economico, essendo pensato soprattutto per le enterprise, ma il suo funzionamento è piuttosto interessante (almeno da quello che ho raccolto stalkerando il loro team di supporto).

Ci sono due modi principali per invocare le capacità di BrowserStack, ed entrambi richiedono di definire la configurazione device/browser accoppiata nel tuo browserstack.yml (qui c’è la mia), dove specifichi la combinazione esatta su cui vuoi eseguire i test e il numero di worker necessari per far girare i test in parallelo.

Il primo metodo, il più facile da applicare a una codebase di test esistente, permette a BrowserStack di eseguire i test contro la tua app sia che sia ospitata in locale, in un ambiente privato o in un setup CI. Viene stabilito un tunnel sicuro per fornire l’accesso, consentendoti di vedere i test eseguirsi direttamente sul tuo setup localhost.

Il secondo approccio consiste nell’eseguire i test direttamente contro gli URL pubblici della tua app deployata. Si inserisce naturalmente nella fase di testing post-deployment della tua CI pipeline. Una volta che la tua app è live su un ambiente di staging o di produzione, BrowserStack può eseguire subito la tua suite su device e browser reali, eliminando la necessità del tunneling. È un modo diretto per validare che ciò che è appena stato deployato funzioni davvero sul campo.

In entrambi gli approcci, i server di BrowserStack ricevono la tua lista di device richiesti, interrogano un indice globale per verificarne la disponibilità e poi allocano nodi corrispondenti nella loro infrastruttura per la tua “build” (il loro termine per la versione dell’app sotto test). Da quello che ho capito, se richiedi 5 worker paralleli sullo stesso tipo di device, il sistema proverà ad allocare 5 device fisici distinti per eseguire quei test in parallelo.

Naturalmente, per ridurre la contesa con altri utenti, BrowserStack offre anche una device farm privata a prezzo più alto, dove device specifici sono riservati in esclusiva alla tua organizzazione.

__wf_reserved_inherit

Bug reali intercettati

Un esempio concreto del tipo di problemi reali che abbiamo scoperto con BrowserStack è emerso testando su un iPhone 15: l’intera app crashava a causa di un’incompatibilità tra Safari e la browser API AbortSignal.timeout(). La stavamo usando in tutte le nostre chiamate fetch, dando per scontato un supporto ampio, ma è saltato fuori che WebKit ha introdotto questa feature solo a partire dalla versione 16, come puoi verificare da te qui. Così, nonostante la nostra passione per l’adozione delle API più recenti, siamo stati costretti a implementare una funzione custom come workaround.

Un altro esempio di bug che sfugge ai viewport simulati si trova nel repository template che ho creato per una demo. La pagina include un layout overflow deliberato che rompe la UX sui device con viewport sotto i 500 px. Se l’overflow si verifica, lo sfondo diventa rosso; altrimenti resta blu. L’unico test del repository controlla esattamente questo: asserisce il background color del componente per verificare l’assenza di overflow:

const bgColor = await page.evaluate(() => {
  const el = document.querySelector('[data-testid="overflow-container"]');
  return el ? getComputedStyle(el).backgroundColor : null;
});
expect(bgColor).toBe("rgb(82, 169, 184)")

Il colpo di scena: il test passa alla perfezione quando viene eseguito in locale con Playwright sui suoi “mobile device” predefiniti, eppure fallisce su BrowserStack su tutti gli stessi device reali. L’ambiente simulato semplicemente non riproduce il comportamento sottile a livello di pixel che ha l’hardware reale.

__wf_reserved_inherit

Perché passa in locale?

Perché quando Playwright “gira su un iPhone”, in realtà non ci sta girando davvero: sta renderizzando dentro un engine Chromium configurato per imitare le caratteristiche dello schermo dell’iPhone. La differenza chiave sta nel modo in cui il browser interpreta i pixel legati all’hardware rispetto ai pixel simulati.

Su un device reale, il Device Pixel Ratio (DPR) è un valore misurato che deriva dalla densità fisica di pixel del display e dallo scaling di sistema del device. Può essere frazionario (ad esempio 2,25 su Firefox per Android, o 3,00 su Safari per certi iPhone) e può persino variare a seconda del livello di zoom o delle impostazioni di accessibilità dell’OS. Questo valore definisce quanti pixel fisici corrispondono a un pixel CSS, e influenza direttamente il rounding del layout, il sub-pixel rendering e il comportamento dell’overflow.

Playwright, invece, non ha accesso all’hardware reale del display. Applica un deviceScaleFactor, che è semplicemente un DPR emulato incapsulato in un descrittore statico. Quel fattore, che nel mondo Playwright può essere solo un intero, dice a Chromium di fingere che la sua griglia di pixel del device abbia una certa densità (diciamo per esempio 3×) e di esporre window.devicePixelRatio = 3 a JavaScript. Ma sotto, sta comunque renderizzando alla risoluzione del display del tuo laptop, non a quella dell’hardware del device target.

Il risultato è che il viewport di Playwright viene arrotondato a step discreti e idealizzato. Arrotonda sia il viewport CSS sia il fattore di scaling. Quelle piccole discrepanze possono facilmente essere la differenza tra un test di layout responsive che passa e uno che fallisce.

Le difficoltà nell’usare BrowserStack

Il processo di configurazione è una sofferenza. Non la indoro. L’SDK di BrowserStack evolve in fretta, e in appena sei mesi ho già visto almeno cinque update. Quando leggerai queste righe, il mio esempio di configurazione potrebbe essere già superato. Resta comunque un buon punto di partenza se ti serve una baseline funzionante. Se ti aiuta, lascia una ⭐️ sul repo! Mi tiene motivata (e un po’ meno brontolona sui file YAML).

Un altro aspetto da considerare è il pricing. Non è l’opzione più economica in circolazione, ma si colloca nella fascia media rispetto a servizi simili di real-device testing. Lato positivo: il loro team di supporto. Le richieste tecniche in chat di solito ricevono risposta entro 24 ore, il che è un salvavita quando stai debuggando da giorni.

Se hai già attraversato questo percorso in prima persona, magari hai collegato Appium, Sauce Labs o un’altra grid di real-device. Ci farebbe molto piacere sapere come l’hai gestita.

In quali pain point sei incappato? Hai trovato modi intelligenti per mantenere le config future-proof?

Conclusioni

Il real-device testing intercetta ciò che i simulatori non riescono a catturare: quirk del browser, mismatch dell’OS e bug specifici del device che impattano direttamente sui tuoi utenti. Tool come BrowserStack aggiungono un po’ di costo e di attrito nel setup, ma ti risparmiano la caccia ai problemi più in là, in ambiente UAT o produzione. Se l’affidabilità è importante, eseguire i test su hardware reale è uno dei passi di maggior valore che puoi fare, per te e per la tua amatissima zia.

Andrea Gorletta
Andrea Gorletta Software Engineer

Andrea is a full-stack developer with a background in art and design, bringing creativity and originality to every project. Self-taught and driven, she approaches challenges with pragmatism, curiosity, and determination.

NEWSLETTER

Ehi, c'è anche una Newslettero!
Sì, proprio con la o.

Ogni mese scegliamo articoli utili ma anche idee fresche e stimolanti. Proprio quello che vorremmo trovare noi in una newsletter!