Salta al contenuto principale

React Native Testing Library: come testare le app correttamente

Padroneggia React Native Testing Library con setup, query, mocking e consigli per la CI. Crea test affidabili e user-centric per componenti, hook e navigazione.

React Native Testing Library: come testare le app correttamente

La tua suite di test React Native è tutta verde, eppure un utente segnala che toccando “Continue” non succede nulla su un dispositivo reale. Il test del componente ha trovato il bottone, ha chiamato l'handler e ha visualizzato la schermata prevista in un ambiente JavaScript mockato. Non ha mai verificato il prompt nativo dei permessi, il comportamento della tastiera, le animazioni, le API della piattaforma o lo stack di navigazione reale.

That gap is where teams get false confidence. React Native Testing Library è eccellente per testare ciò che un componente renderizza e come risponde all'interazione dell'utente, ma non sostituisce i test su dispositivo o la misurazione delle performance. La strategia vincente è usare ogni layer per i fallimenti che è in grado di esporre.

Indice dei contenuti

Perché il testing user-centric cambia tutto

Un fallimento comune nei test inizia prima ancora che il test venga scritto. Uno sviluppatore ispeziona le props di un componente, accede al suo stato o confronta un grande snapshot perché quei dettagli sono facili da verificare. Il test passa, poi un refactor cambia la struttura del componente senza alterare l'esperienza e la suite si rompe. Peggio ancora, il test può continuare a passare mentre il comportamento visibile all'utente è errato, perché l'asserzione non ha mai descritto ciò che l'utente deve effettivamente vedere.

React Native Testing Library adotta l'approccio opposto. Renderizzi il componente, interagisci con esso tramite un controllo visibile e poi verifichi il risultato che l'utente può osservare. Questo approccio si allinea all'esempio user-centric di React Native Testing Library, così come alle linee guida di testing di React Native per mantenere i test brevi, concentrare ogni test su una sola cosa, separare le responsabilità della view dalla business logic e dallo stato, e preferire l'output visibile o gli helper di accessibilità ai dettagli di implementazione interna.

Un diagramma che illustra i vantaggi del testing user-centric, evidenziando il comportamento dell'utente, la resilienza, l'affidabilità e le best practice.

Test the behavior users depend on

Supponiamo che un form di login disabiliti il pulsante di invio durante una richiesta. Un test fragile potrebbe ispezionare disabled su una specifica istanza di un componente o verificare che una variabile di stato sia cambiata. Un test più robusto preme il controllo accessibile “Sign in”, attende l'indicatore di caricamento e controlla che appaia un messaggio di errore o la schermata di destinazione.

Al secondo test non importa se il form usa stato locale, un reducer, un custom hook o un'implementazione diversa del pulsante. Gli importa che l'app comunichi il risultato corretto.

Regola pratica: Se un utente non può osservarlo, chiediti se debba far parte di un test del comportamento del componente.

Le query basate su ruoli e label di accessibilità spingono anche verso un migliore code del prodotto. Una schermata che espone label significative è più facile da usare con le tecnologie assistive e più semplice da testare. Questo legame conta quando valuti la più ampia esperienza utente dell'app, perché testabilità e usabilità spesso migliorano insieme.

Scopri cosa i test che passano non dimostrano

Un test del componente centrato sull'utente può dimostrare che JavaScript renderizza il ramo previsto e risponde a una pressione simulata. Non può però dimostrare che un prompt biometrico si apra correttamente, che una fotocamera nativa restituisca un risultato utilizzabile o che un flusso di pagamento sopravviva ai cicli di vita specifici della piattaforma.

Questo limite non è un difetto della libreria. È un motivo per mantenere onesti i vari livelli di test. Usa RNTL per il comportamento dei componenti, poi riserva i test basati sul dispositivo per i flussi in cui l'integrazione nativa, la navigazione reale, i permessi, l'autenticazione, i pagamenti o le funzionalità core dell'app possono cambiare l'esito.

Installazione e configurazione che funziona davvero

Il setup moderno inizia con il package scoped:

@testing-library/react-native

Il vecchio react-native-testing-library nome del package npm esiste ancora come reperto storico, ma i progetti attuali dovrebbero usare il package scoped mantenuto all'interno della Testing Library family. Il progetto repository GitHub lo descrive come utility per React Native per incoraggiare buone pratiche di testing, e la sua cronologia dei rilasci mostra che la libreria continua a evolversi insieme a React Native invece di rimanere un semplice helper statico.

Installa la libreria nel tuo ambiente Jest esistente. I progetti Expo usano comunemente l'Expo Jest preset, mentre i progetti bare React Native possono usare il React Native preset:

npm install --save-dev @testing-library/react-native jest

Per Expo, aggiungi il preset che il tuo progetto sta già usando:

{
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "preset": "jest-expo"
  }
}

Per un'applicazione bare React Native, usa invece la corrispondente configurazione React Native Jest. Mantieni il preset allineato alla versione del framework. Molti errori che sembrano problemi di RNTL derivano da un mismatch del React renderer, del Babel transform o del preset di Jest.

Mantieni i file di setup espliciti

Inserisci la configurazione dell'ambiente condiviso in un file di setup invece di ripetere i mock in ogni test:

// jest.setup.js
import '@testing-library/react-native/extend-expect';

Poi fai riferimento ad esso da Jest:

{
  "jest": {
    "preset": "jest-expo",
    "setupFilesAfterEnv": ["<rootDir>/jest.setup.js"]
  }
}

Se la tua app usa React Navigation, Reanimated, la gestione dei gesti, safe-area context o moduli di storage, configura solo i mock necessari al tuo ambiente di test. Un mock globale che altera il comportamento dell'app può rendere più facile superare ogni test, ma compromette l'affidabilità dell'intera suite.

Anche TypeScript richiede attenzione. Assicurati che Jest trasformi .ts e .tsx i file tramite il preset o la tua configurazione Babel, e mantieni i tipi di test accessibili al compilatore. Un test che gira localmente ma non viene controllato dal type-checker può nascondere nomi di query errati, parametri di navigazione non validi o forme di mock non sicure.

La cronologia dei rilasci è utile per diagnosticare vecchi consigli di configurazione. Il progetto elenca 127 rilasci, con v14.0.1 taggato il 2026-06-23, mentre v12.9.0, rilasciato il 2024-11-27, ha aggiunto il supporto ufficiale per React Native 0.77 ed Expo 52. La linea alpha v14 a marzo 2025 è passata a Universal Test Renderer dal deprecato React Test Renderer, preparandosi al supporto esclusivo per React 19. Questi dettagli sono presenti nella cronologia dei rilasci di RNTL, quindi non copiare una dipendenza del renderer da un tutorial datato senza prima controllare le versioni della tua app.

Per una base Jest pratica, confronta la tua configurazione con questa guida ai unit test in JavaScript, poi esegui un piccolo test su un componente prima di aggiungere la navigazione e i mock nativi.

Un'infografica che mostra il percorso di installazione in cinque step per configurare il progetto React Native Testing Library.

Spiegazione delle API core: Query e Assertion

I test RNTL diventano affidabili quando le query corrispondono a ciò che un utente può vedere, trovare e usare. Il API è minimo, ma scegliere un selettore che espone dettagli di implementazione può rendere fuorviante un test superato.

Inizia con render:

const screen = render(<LoginForm />);

Scegli la query più pertinente per l'utente. Prediligi query orientate all'accessibilità se il componente le espone, usa il testo visibile quando il comportamento dipende dal testo e riserva testID per i casi in cui non esiste un selettore significativo per l'utente o quando è necessario un hook di integrazione stabile.

Un grafico a piramide che mostra l'ordine di priorità consigliato per le query nell'automazione dei test software.

Scegli la query in base al timing e all'intento

Ogni famiglia di query ha uno scopo specifico:

  • Presenza sincrona: Usa getByRole, getByText, o un'altra getBy query quando l'elemento dovrebbe già esistere. Il test fallisce immediatamente se non lo trova.
  • Apparizione asincrona: Usa findByRole o findByText quando il rendering o un'interazione causa un aggiornamento asincrono.
  • Verifica assenza: Usa queryByText o queryByTestId quando un elemento potrebbe mancare e ci si aspetta un risultato null invece di un'eccezione.
  • Selettori di fallback: Usa getByTestId deliberatamente. Fornisce ai controlli complessi un hook stabile, ma non deve sostituire le label accessibili in tutta l'app.

Per test di eventi mirati, fireEvent.press è diretto:

fireEvent.press(screen.getByRole('button', { name: 'Save' }));

Usa userEvent quando la versione installata supporta una sequenza di interazione più realistica. In ogni caso, verifica la UI risultante:

expect(await screen.findByText('Saved')).toBeTruthy();

Un'asserzione sull'handler è adatta a un componente il cui contratto pubblico è una callback di un evento. È debole se usata come unica prova del corretto user journey.

Preferisci asserzioni specifiche ai snapshot

Le buone asserzioni descrivono lo schermo:

expect(screen.getByText('Account created')).toBeTruthy();
expect(screen.getByRole('button', { name: 'Continue' })).toBeEnabled();

Possono anche verificare lo stato di accessibilità, la selezione e i feedback di validazione visibili. Evita di controllare la logica interna:

expect(screen.getByTestId('submit-button').props.onPress).toBeDefined();

Quell'assertion prova l'esistenza di una prop, non che la feature funzioni. Snapshot piccoli e mirati possono rilevare cambiamenti strutturali, mentre snapshot di navigazione o schermate troppo ampi generano spesso review rumorose e facilitano l'approvazione di update non spiegati.

Il pacchetto Testing Library fa parte della più ampia testing-library organizzazione npm. I suoi pacchetti attivi includono @testing-library/react-native, con la versione 13.3.3 pubblicata nel 2026, in base alle informazioni del repository del progetto. Le convenzioni di query condivise aiutano tra diverse piattaforme, ma non decidono quale assertion rappresenti il comportamento del tuo prodotto.

Per un confronto più ampio sulle pratiche di testing dei componenti con Jest, consulta questa guida a unit testing di React. RNTL si ferma comunque al confine del componente renderizzato in JavaScript. Permessi nativi, stack di navigazione reali, tastiere del dispositivo, timing dei frame e gestione della memoria richiedono tool E2E o di performance piuttosto che ulteriori mock di componenti.

Il video qui sotto mostra il workflow di query e assertion nel contesto reale.

Pattern pratici per componenti, hook e navigazione

Una suite di test efficace segue la forma dell'applicazione. I componenti presentazionali richiedono test diretti sul comportamento, gli hook richiedono input e output controllati, la navigazione richiede un provider sufficientemente realistico e i moduli nativi richiedono mock che restino chiaramente distinti dalla verifica su dispositivo reale.

Un laptop moderno su una scrivania in legno che mostra React Native code e il mockup di un'app mobile.

Componenti presentazionali

Tieni il test di un componente vicino al suo contratto pubblico:

const onSelect = jest.fn();

render(
  <PlanCard
    title="Team"
    description="Shared workspace"
    onSelect={onSelect}
  />
);

fireEvent.press(screen.getByRole('button', { name: 'Choose Team' }));

expect(onSelect).toHaveBeenCalled();

L'etichetta esatta deve corrispondere alla tua UI. L'importante è che il test trovi il controllo come farebbe un utente o un servizio di accessibilità e confermi il risultato visibile o la callback che conta. Non mockare ogni componente figlio di default. Mocka solo i confini costosi o non pertinenti quando oscurano il comportamento in esame.

Per un hook custom, usa renderHook quando la versione di RNTL installata lo fornisce:

const { result } = renderHook(() => useSearch());

await act(async () => {
  await result.current.submit('query');
});

expect(result.current.status).toBe('success');

Il test dell'hook deve controllare il confine di rete o del repository, non riprodurre l'intera app. Testa la schermata separatamente per capire se lo stato dell'hook diventa UI utile.

Per il comportamento della navigazione, renderizzare una schermata dentro un vero NavigationContainer e un piccolo test navigator è spesso più utile che mockare ogni metodo di navigazione. Premi un controllo visibile, attendi il contenuto di destinazione e verifica l'output della nuova schermata. Un approccio diretto useNavigation il mock è ancora adatto per un piccolo bottone che deve solo dispatchare una rotta tipizzata, ma non convaliderà la registrazione della rotta, i parametri o il comportamento del navigator annidato.

I dati asincroni meritano la stessa disciplina. Mocka la risposta di API o del repository, renderizza lo screen, verifica lo stato di loading, risolvi la richiesta e poi verifica l'output di successo o errore. Usa findBy le query per gli elementi attesi e rendi esplicite le richieste rifiutate, altrimenti un test potrebbe passare perché il componente non ha mai raggiunto il ramo previsto.

Mock per moduli nativi

I mock per AsyncStorage, permessi, fotocamere, biometria e API di piattaforma sono utili per test JavaScript deterministici. Non sono una prova che la feature nativa funzioni. Mantieni il comportamento del mock vicino al contratto del modulo, resetta le chiamate tra i test e includi risposte di errore invece di modellare solo lo happy path.

Scenario Ideale con RNTL Richiede validazione E2E
Validazione form ed errori visibili Sì Di solito no
UI di loading, successo ed errore da un repository mockato Sì Per flussi di produzione critici
Navigazione tra gli screen registrati Sì, con un test navigator Sì, se contano gesture, deep link o comportamento della piattaforma
Decisioni di stato su AsyncStorage Sì, con un mock controllato Sì, se avvio e persistenza interagiscono con il lifecycle nativo
Camera, biometria, permessi o API di piattaforma Logica di branching e fallback JS Sì, su dispositivi reali o rappresentativi
Layout, performance di rendering e code nativo No Sì, con un dispositivo o tool specializzati

Il limite è pratico: mocka la dipendenza per testare la logica JavaScript, poi esegui un test su dispositivo per confermare il comportamento reale della dipendenza.

Debug di test flaky in CI e controlli di performance

Un test flaky spesso indica tempo non controllato, stato condiviso o un'assertion che va in race con la UI. Identifica la condizione prima di aumentare i timeout. Un timeout più lungo può nascondere il problema di scheduling e rallentare l'intera suite.

Usa findBy per elementi che dovrebbero apparire dopo un update. Usa waitFor per condizioni di stato o chiamate mock. Se i fake timer sono attivi, avanzali nel punto in cui l'interazione lo richiede e ripristina i timer reali subito dopo. Un act warning significa che React ha rilevato un update al di fuori del suo boundary di interazione previsto. Correggi la mancanza di await, dell'interazione utente o del flush dei timer, invece di ignorare il warning.

Rendi riproducibili i fallimenti della CI

Un job CI affidabile installa dai lockfile, esegue lo stesso comando Jest usato in locale e isola lo stato dei mock. Pulisci le chiamate mock tra i test, resetta i moduli quando lo stato a livello di modulo influenza il comportamento e rimuovi le dipendenze dall'ordine di esecuzione. La cache di Jest velocizza il feedback, ma i cambiamenti a dipendenze o configurazioni richiedono un'adeguata invalidazione della cache.

I fallimenti che avvengono solo in CI richiedono un confronto dell'ambiente prima di riscrivere un componente. Controlla Node, il package manager, le impostazioni dei worker di Jest, la configurazione dei timer e le variabili d'ambiente. Se possibile, riproduci lo stesso comando localmente, poi riduci il test fallito alla minima interazione che espone la differenza.

L'osservabilità copre una lacuna differente. Il monitoraggio della salute dell'app fornisce il contesto degli errori in produzione che i test dei componenti mockati non possono riprodurre, inclusi i fallimenti legati a dispositivi reali e integrazioni native.

I flussi sensibili alla sicurezza richiedono un secondo confine. Usa i test dei componenti per la validazione e le transizioni di stato, poi aggiungi una copertura a livello di dispositivo per il passaggio e il comportamento nativo. Per i flussi code una tantum, consulta la guida su come testare i flussi di verifica SMS quando quell'integrazione fa parte del percorso.

Tratta le performance come misurazioni, non come asserzioni

Un test funzionale può confermare che una lista venga renderizzata. Non può determinare con affidabilità se un refactor abbia cambiato la durata o il numero di render, perché il runtime del test aggiunge rumore. Reassure misura quei valori per uno scenario, ripete lo scenario per ridurre la varianza e applica un'analisi statistica prima di segnalare un cambiamento significativo. La sua documentazione sui test di performance include anche report adatti per la CI e la revisione delle pull request.

Evita assertion fisse come “il render deve finire sotto una certa soglia”. Un worker CI sovraccarico può causare un falso negativo, mentre una soglia troppo permissiva può ignorare una regressione reale. Usa misurazioni ripetute per i segnali di regressione, poi analizza il componente e il profilo del dispositivo quando il confronto mostra una differenza significativa.

Regola di misurazione: I test funzionali verificano se il comportamento è corretto. Gli strumenti di performance verificano se uno scenario misurato è cambiato. Mantieni queste due domande separate.

Mettiamo tutto insieme e guardiamo avanti

React Native Testing Library fa parte dello strato rapido e ampio della tua strategia di test. Dovrebbe coprire il comportamento dei componenti, i cambiamenti di stato visibili, l'accessibilità, la validazione, gli stati dei dati mockati e l'integrazione tra componenti JavaScript. Mantieni questi test focalizzati su ciò che l'utente può osservare, facendo sì che i fallimenti indichino un comportamento specifico piuttosto che un intero albero di rendering.

Lo strato più piccolo basato sui dispositivi dovrebbe proteggere i flussi in cui i mock possono ingannare. React Native panoramica dei test precisa che RNTL non fornisce un runtime React Native completo e non può testare le feature native. La stessa guida consiglia di affiancare i test dei componenti a strumenti E2E come Detox per i flussi critici, inclusi autenticazione, pagamenti e funzionalità core dell'app.

Un percorso di migrazione pratico

Non è necessario riscrivere un'intera suite in un colpo solo.

  1. Mantieni i test di business più preziosi. Sposta lo stato puro e la logica di dominio in unit test mirati per ottenere feedback chiari.
  2. Sostituisci prima le asserzioni sull'implementazione. Trasforma i controlli su prop e stato interno in output visibile, stato di accessibilità ed esiti delle interazioni.
  3. Riduci i snapshot. Mantieni solo gli snapshot che i reviewer riescono a capire e mantenere.
  4. Aggiungi copertura per i confini nativi. Per ogni modulo mockato importante, identifica il comportamento del dispositivo che richiede ancora validazione.
  5. Proteggi i flussi critici. Aggiungi copertura E2E per autenticazione, pagamenti, navigazione principale, permessi e altri flussi in cui il comportamento nativo può cambiare il risultato.
  6. Misura separatamente le schermate sensibili. Usa confronti ripetuti sulle performance per liste, feed e percorsi di render onerosi, invece di indovinare dai tempi di Jest.

Per rilasciare con fiducia, collega la suite a test di integrazione CI/CD e richiede il giusto livello di test prima del rilascio. Se un fix JavaScript o di un asset deve raggiungere gli utenti dopo la validazione, Capgo può distribuire web bundle firmati a canali specifici per applicazioni CapacitorJS ed Electron, con controlli di rollout e protezione dal rollback. Questo workflow di distribuzione non sostituisce i test su dispositivi React Native, ma illustra lo stesso principio: validare il comportamento al livello in cui viene eseguito.

La domanda corretta non è se React Native Testing Library possa testare l'intera app. Non può, e il confine ufficiale è utile. La domanda giusta è se ogni rischio importante abbia un test in esecuzione nell'ambiente capace di esporlo.


Capgo collega le modifiche validate di JavaScript e asset a una distribuzione controllata per i team CapacitorJS ed Electron, con canali mirati, visibilità del rollout e protezione dai rollback. Visita Capgo per vedere come può integrarsi nel tuo workflow di testing dei componenti, E2E e CI.

Live updates per le app Capacitor

Quando un bug nel web-layer è live, rilascia la fix tramite Capgo invece di aspettare giorni l'approvazione degli store. Gli utenti ricevono l'update in background, mentre le modifiche native restano nel normale processo di review.

Ultimi articoli dal blog

Capgo ti offre gli insight migliori che ti servono per creare un'app mobile davvero professionale.