Test dei componenti in React: cosa e come testare con Jest e Enzyme.

Questo articolo sul test dei componenti di reazione è stato scritto da Alona Pysarenko - Ingegnere frontend di Django Stars.
Leggi l'articolo originale sul blog di Django Stars.

Testare i componenti di React può essere impegnativo sia per i principianti che per gli sviluppatori esperti che hanno già lavorato con i test. Potrebbe essere interessante confrontare i tuoi approcci con quelli che usiamo nel nostro progetto. Per coprire la base di codice, devi sapere quali componenti devono essere testati e quale codice esattamente nel componente deve essere coperto.

Durante questo articolo tratterò i seguenti argomenti:

  • Definire l'ordine corretto dei test dei componenti in base alla struttura del progetto
  • Trova cosa omettere nella copertura del test (cosa non testare)
  • Identificare la necessità di Snapshot Testing
  • Definire cosa testare nel componente e in quale ordine
  • Fornire esempi dettagliati di codice personalizzato

L'articolo richiede la conoscenza della configurazione di Jest e Enzyme. Informazioni sull'installazione e sulla configurazione sono facilmente reperibili sui loro siti Web ufficiali.

Supponiamo il caso seguente: è necessario coprire la base di codice del progetto con i test. Con cosa dovresti iniziare e cosa dovresti ottenere alla fine del test? Copertura test al 100%? È il punto di riferimento a cui dovresti aspirare, ma nella maggior parte delle situazioni non lo capirai.

Perché? Perché non dovresti testare tutto il codice. Scopriremo perché e cosa dovrebbe essere escluso dai test. Ancora di più, la copertura del test al 100% non garantisce sempre che il componente sia completamente testato. Non è inoltre garantito che ti avviserà se qualcosa è stato modificato. Non cercare le percentuali, evita di scrivere test falsi e cerca di non perdere i dettagli del componente principale.

Definire l'ordine corretto dei test dei componenti in base alla struttura del progetto

Discutiamo di questa domanda nella parte successiva della struttura del progetto:

Ho preso la directory condivisa perché è la più importante. È costituito dai componenti utilizzati in diverse pagine del progetto. Sono riutilizzabili e normalmente sono piccoli e non complessi. Se uno o un altro componente si guasta, si verificherà un guasto in altri punti. Ecco perché dovremmo essere sicuri che siano stati scritti correttamente. La struttura di questa directory è divisa in più cartelle, ognuna contenente componenti.

Come definire l'ordine corretto dei test dei componenti nella directory condivisa:

  • Segui sempre la regola di passare dal semplice al complesso. Analizza ogni directory e definisci quali componenti sono indipendenti, ovvero il loro rendering non dipende dagli altri componenti. Sono completati automaticamente e possono essere utilizzati separatamente come singola unità. Dalla struttura sopra, è la directory degli input nella cartella dei moduli. Contiene componenti di input in moduli redux, come TextInput, SelectInput, CheckboxInput, DateInput, ecc.
  • Successivamente, è necessario definire i componenti ausiliari che vengono spesso utilizzati nei componenti di input, ma devono essere testati separatamente. Questa è la directory utils. I componenti in questa cartella non sono complicati, ma molto importanti. Sono spesso riutilizzabili e aiutano con azioni ripetute.
  • Il prossimo passo è definire quali componenti possono essere usati anche in modo indipendente. Se presenti, portali per i test. Dalla nostra struttura, sono i widget, i piccoli componenti con funzionalità semplice. Saranno il terzo elemento in coda per la copertura del test.
  • Inoltre, analizzare il resto delle directory e definire componenti più complessi, che possono essere utilizzati indipendentemente o insieme ad altri componenti. È la directory modals nel nostro caso. Questi componenti saranno spiegati in dettaglio di seguito.
  • I componenti più complessi vengono lasciati alla fine. Sono la directory e i campi hoc dalla cartella dei moduli. Come definisci quale dovrebbe essere testato per primo? Prendo la directory da cui i componenti sono già stati utilizzati nei componenti testati. Pertanto, il componente dalla directory hoc era presente nel componente widget. Ecco perché so già dove e per quale scopo vengono utilizzate questa directory e i suoi componenti.
  • L'ultima è la cartella dei campi. Contiene componenti collegati con redux-form.

L'ordine dei componenti finali (basato sul nostro esempio) sarà simile al seguente:

Seguendo questo ordine, si aumenta passo dopo passo la complessità dei componenti testati. Pertanto, quando si tratta di operare con i componenti più complessi, si sa già come si comportano i più piccoli.

Non prendere per il test, ad esempio, il campo "array", se non si è sicuri su come testare il campo "testo". Non prendere componenti decorati con il modulo redux se non hai testato il campo "modulo" stesso.

Sii coerente nelle tue scelte, non prendere il primo componente che ti viene in mente e cambiare la logica. Naturalmente, la struttura del tuo progetto può differire. Può avere altri nomi di directory o può avere componenti, azioni e riduttori aggiuntivi, ma la logica di definizione dell'ordine per testare i componenti è la stessa.

Definiamo cosa deve essere omesso nella copertura del test:

  • Librerie di terze parti. Non testare le funzionalità prese da un'altra libreria. Non sei responsabile per quel codice. Salta o imita l'implementazione se ne hai bisogno per testare il tuo codice.
  • Costanti. Il nome parla da solo. Non sono modificabili. Sono insiemi di codice statico che non intendono variare.
  • Stili incorporati (se li usi nel tuo componente). Per testare gli stili incorporati, è necessario duplicare l'oggetto con gli stili nel test. Se gli stili degli oggetti cambiano, è necessario modificarli anche nel test. Non duplicare il codice di un componente nei test. Non ti ricorderai mai di cambiarlo nei test. Inoltre, il tuo collega non si accorgerà mai che c'è stata una duplicazione. Nella maggior parte dei casi, gli stili in linea non cambiano il comportamento del componente, quindi non dovrebbero essere testati. Può esserci un'eccezione se i tuoi stili cambiano in modo dinamico.
  • Cose non correlate al componente testato. Salta la copertura con i componenti di test che sono stati importati nel componente testato. Fai attenzione se è avvolto in un altro. Non testare il wrapper, basta analizzarlo e testarlo separatamente.

Quindi come scrivi effettivamente i test? Combino due approcci di test:

  • Test di istantanee
  • Test della logica dei componenti

Ne discuterò entrambi ora.

Come testare con le istantanee

Il test snapshot è un utile strumento di test nel caso in cui si desideri essere sicuri che l'interfaccia utente non sia cambiata. Quando si affronta questo strumento di test per la prima volta, potresti avere domande relative all'organizzazione e alla gestione delle istantanee. Il principio è molto semplice, ma sfortunatamente non è stato completamente descritto da nessuna parte.

Passaggio 1. Scrivere il test per il componente e nel blocco di attesa utilizzare il metodo .toMatchSnapshot () che crea lo snapshot stesso:

it ('render correttamente componente di testo', () => {
    const TextInputComponent = renderer.create (). toJSON ();
    si aspettano (TextInputComponent) .toMatchSnapshot ();
});

Passaggio 2. Quando si esegue il test per la prima volta su un livello, insieme al test, verrà creata una directory denominata __snapshots__ con il file generato automaticamente all'interno con extension.snap.

L'istantanea si presenta così:

// Jest Snapshot v1, https://goo.gl/fbAQLP
export [`Render TextInput correttamente componente 1`] =`

`;

Passaggio 3. Inserire l'istantanea nel repository e archiviarla insieme al test.

Se il componente è stato modificato, è sufficiente aggiornare lo snapshot con —updateSnapshot flag o utilizzare il flag u di forma breve.

Quindi viene creata l'istantanea: come funziona?

Consideriamo due casi:

1. Il componente è stato modificato

  • Esegui test
  • Viene creata una nuova istantanea, si confronta con l'istantanea generata automaticamente memorizzata nella directory __snapshots__
  • Test non riusciti perché lo snapshot è diverso

2. Il componente non è stato modificato

  • Esegui test
  • Viene creata una nuova istantanea, si confronta con l'istantanea generata automaticamente memorizzata nella directory __snapshots__
  • Test passati perché lo snapshot è identico

Va tutto bene quando testiamo un piccolo componente senza logica (solo rendering dell'interfaccia utente). Ma come dimostra la pratica, non ci sono tali componenti su progetti reali. Se esistono, sono pochi.

Ci sono abbastanza snapshot per il test completo dei componenti?

Principali istruzioni per i test dei componenti

1. Un componente dovrebbe avere solo un'istantanea.

Se uno snapshot fallisce, molto probabilmente anche gli altri falliranno. Non creare e archiviare un mucchio di istantanee non necessarie che intasano lo spazio e confondono gli sviluppatori che leggeranno i tuoi test dopo di te.

Naturalmente, ci sono eccezioni quando è necessario testare il comportamento di un componente in due stati: ad esempio, nello stato del componente prima di aprire il pop-up e dopo l'apertura.

Tuttavia, anche una tale variante può sempre essere sostituita da questa: il primo test memorizza lo stato predefinito del componente senza il popup nell'istantanea e il secondo test simula l'evento e verifica la presenza di una particolare classe. In questo modo, puoi facilmente bypassare la creazione di più istantanee.

2. Puntelli di prova

Di norma, divido il test degli oggetti di scena in due test:

  • Innanzitutto, controlla il rendering dei valori di prop predefiniti. Quando viene reso il componente, mi aspetto che un valore sia uguale a defaultProps nel caso in cui questo prop abbia defaultProps.
  • In secondo luogo, controlla il valore personalizzato dell'elica. Ho impostato il mio valore e mi aspetto che venga ricevuto dopo il rendering del componente.

3. Test dei tipi di dati

Per testare quale tipo di dati arriva negli oggetti di scena o che tipo di dati si ottengono dopo determinate azioni, possiamo usare la libreria speciale jest-extended (Additional Jest matchers), che ha un set esteso di corrispondenze che sono assenti nel Scherzare. Con questa libreria, testare i tipi di dati è molto più semplice e divertente.

Testare i proptype, d'altra parte, è una domanda contraddittoria. Alcuni sviluppatori possono contestare il test dei proptypes perché è un pacchetto di terze parti e non dovrebbe essere testato. Tuttavia, insisto per testare i proptype dei componenti perché non collaudo la funzionalità del pacchetto stesso. Invece, mi assicuro solo che i proptype siano corretti. Il tipo di dati è una parte di programmazione molto importante e non deve essere ignorato.

4. Test degli eventi

Dopo aver creato un'istantanea e aver coperto i puntelli con i test, puoi essere sicuro che il componente verrà visualizzato correttamente. Ma questo non è sufficiente per la copertura completa nel caso in cui ci siano eventi nel componente.

Puoi controllare l'evento in diversi modi. I più utilizzati sono:

  • mock event => simulate it => prevede evento chiamato
  • mock event => simulate event with params => prevede che l'evento sia stato chiamato con i parametri passati
  • passa gli oggetti di scena necessari => render componente => simula evento => aspettati un determinato comportamento sull'evento chiamato

5. Condizioni di prova

Molto spesso puoi avere condizioni per l'output di una particolare classe, renderizzare una certa sezione del codice, trasferire gli oggetti di scena richiesti e così via. Non dimenticare questo, perché con i valori predefiniti, solo un ramo passerà il test, mentre il secondo rimarrà non testato.

In componenti complessi con calcoli e molte condizioni, puoi perdere alcuni rami. Per assicurarsi che tutte le parti del codice siano coperte dai test, utilizzare uno strumento di copertura dei test e verificare visivamente quali rami sono coperti e quali no.

6. Stato del test

Per verificare lo stato, nella maggior parte dei casi è necessario scrivere due test:

  • Il primo controlla lo stato corrente.
  • Il secondo controlla lo stato dopo aver chiamato un evento. Render component => call function direttamente nel test => controlla come lo stato è cambiato. Per chiamare la funzione del componente, è necessario ottenere un'istanza del componente e solo successivamente chiamare i suoi metodi (l'esempio è mostrato nel test successivo).

Dopo aver esaminato questo elenco di istruzioni, il componente sarà coperto dal 90 al 100%. Lascio il 10% per casi speciali che non sono stati descritti nell'articolo, ma possono verificarsi nel codice.

Esempi di test

Passiamo agli esempi e copriamo i componenti con i test come abbiamo descritto sopra passo dopo passo.

1. Test di un componente da moduli / input.

Prendi un componente dalla directory di moduli / input. Lascia che sia DateInput.js, il componente per il campo datepicker.

Elenco di codici per il componente testato: DateInput.js
Sembra:

Il componente DateInput utilizza la libreria reag-datepicker, con due utilità:

  • valueToDate (converte il valore in data)
  • dateToValue (converte la data in valore)

Il pacchetto è per manipolare con data e PropTypes è per controllare i puntelli React.

Secondo il codice del componente, possiamo vedere l'elenco di oggetti di scena predefiniti che aiutano a rendere il componente reso:

const defaultProps = {
    inputClassName: 'input-custom',
    mesi Mostrato: 1,
    dateFormat: 'GG.MM.AAAA',
    showMonthYearsDropdowns: false,
    minDate: moment ()
};

Tutti gli oggetti di scena sono appropriati per la creazione dell'istantanea, tranne uno: minDate: moment (). moment () ci fornirà la data corrente ogni volta che eseguiamo il test e lo snapshot fallirà perché memorizza una data obsoleta. La soluzione è di deridere questo valore:

const defaultProps = {
    minDate: moment (0)
}

Abbiamo bisogno del puntello minDate in ogni componente renderizzato. Per evitare la duplicazione di oggetti di scena, creo HOC che riceve defaultProps e restituisce un componente carino:

importa TestDateInput da '../DateInput';
const DateInput = (props) =>
    ;

Non dimenticare il fuso orario, specialmente se i tuoi test verranno eseguiti da sviluppatori di un altro Paese in un fuso orario diverso. Riceveranno il valore deriso, ma con uno spostamento del fuso orario. La soluzione è impostare un fuso orario predefinito:

const moment = require.requireActual ('moment-timezone'). tz.setDefault ('America / Los_Angeles')

Ora il componente di input della data è pronto per il test:

1.Crea prima l'istantanea:

it ('rendering componente data corretto', () => {
    const DateInputComponent = renderer.create (). toJSON ();
    si aspettano (DateInputComponent) .toMatchSnapshot ();
});

2. Puntelli di prova:

Guarda tra gli oggetti di scena e trova quelli importanti. Il primo oggetto da testare è showMonthYearsDropdowns. Se impostato su true, viene visualizzato il menu a discesa per mese e anni:

it ("visualizza i menu a discesa di mese e anni visualizzati", () => {
    const props = {
            showMonthYearsDropdowns: true
        },
        DateInputComponent = mount ().find('.datepicker ');
    si aspettano (DateInputComponent.hasClass ( 'reagire-datepicker-nascondere mesi')) toEqual (true).;
});

Prova il valore di prop null. Questo controllo è necessario per garantire che il componente venga visualizzato senza un valore definito:

it ('rendering data inserito correttamente con valore null', () => {
    const props = {
            valore: null
        },
        DateInputComponent = mount ();
    aspettarsi ((DateInputComponent) .prop ( 'valore')) toEqual (nullo).;
});

3.Proprietà di prova per valore, data prevista per la stringa:

it ('controlla il tipo di valore', () => {
    const props = {
            valore: '10 .03.2018 '
        },
        DateInputComponent = mount ();
    si aspettano (DateInputComponent.prop ( 'valore')) toBeString ().;
});

4. Eventi di prova:

Innanzitutto, controlla l'evento onChange.

  • mock on Cambia callback
  • rendere il componente di input della data
  • simula l'evento change con un nuovo valore target
  • e infine controlla che l'evento onChange sia stato chiamato con un nuovo valore.
it ('controlla il callback onChange', () => {
    const onChange = jest.fn (),
        oggetti di scena = {
            valore: '20 .01.2018 ',
            onChange
        },
        DateInputComponent = mount ().find('input ');
    DateInputComponent.simulate ('change', {target: {value: moment ('2018-01-22')}});
    si aspettano (onChange) .toHaveBeenCalledWith ('22 .01.2018' );
});

Successivamente, assicurarsi che il popup del datepicker si apra dopo un clic sull'input della data. Per questo, trova data input => simula click event => e aspetta il popup quando è presente la classe .react-datepicker.

it ('controlla popup DatePicker aperto', () => {
    const DateComponent = mount (),
        dateInput = DateComponent.find ("input [type = 'text']");
    dateInput.simulate ( 'click');
    si aspettano (DateComponent.find () 'reagire-datepicker.') toHaveLength (1).;
});

Elenco completo dei test: DateInput.test.js

2. Test di utilità:

Elenco dei codici per l'utilità testata: valueToDate.js

Lo scopo di questa utility è trasformare un valore in una data con un formato personalizzato.

Prima di tutto, analizziamo l'utilità indicata e definiamo i casi principali per i test:

  1. Secondo lo scopo di questa utility, trasforma il valore, quindi dobbiamo controllare questo valore:
  • Nel caso in cui il valore non sia definito: dobbiamo essere sicuri che l'utilità non restituirà un'eccezione (errore).
  • Nel caso in cui sia definito un valore: dobbiamo verificare che l'utilità restituisca la data del momento.

2. Il valore restituito dovrebbe appartenere alla classe moment. Ecco perché dovrebbe essere un esempio del momento.

3. Il secondo argomento è dateFormat. Impostalo come costante prima dei test. Ecco perché verrà passato in ogni test e restituirà il valore in base al formato della data. Dovremmo testare DataFormat separatamente? Suppongo di no. Questo argomento è facoltativo: se non impostiamo dateFormat, l'utilità non si interromperà e restituirà semplicemente la data di ritorno nel formato predefinito. È un lavoro momentaneo, non dovremmo testare librerie di terze parti. Come ho detto prima, non dovremmo dimenticare il momento-fuso orario; è un punto molto importante, soprattutto per gli sviluppatori di diversi fusi orari.

Facciamo il codice:

  1. Scrivi il test per il primo caso. Quando non abbiamo un valore, è vuoto.
const format = 'GG.MM.AAAA';
it ('render utility valueToDate con valore vuoto', () => {
    valore const = valueToDate ('', formato);
    aspettarsi (valore) .toEqual (null);
});

2. Controlla se il valore è definito.

const date = '21 .11.2015 ',
      formato = "GG.MM.AAAA";
it ('render utility valueToDate con valore definito', () => {
    const value = valueToDate (data, formato);
    prevedono (valore) .toEquale (momento (data, formato));
});

3. Controlla se il valore appartiene alla classe moment.

const date = '21 .11.2015 ',
    format = 'GG.MM.AAAA';
it ('check value is instanceof moment', () => {
    const value = valueToDate (data, formato);
    prevedere (valore istanza del momento) .toBeTruthy ();
});

Elenco completo dei test: valueToDate.test.js

3. Test dei widget

Per i test sui widget, ho preso un componente Spinner.

Elenco di codici per widget testati: Spinner.js

Somiglia a questo:

Lo spinner non è richiesto nella spiegazione, poiché quasi tutte le risorse Web hanno questo componente.

Quindi se andiamo a scrivere test:

  1. Primo passo: crea un'istantanea:
it ('render correttamente componente Spinner', () => {
   const SpinnerComponent = mount ();
   si aspettano (SpinnerComponent) .toMatchSnapshot ();
});

2. Puntelli di prova:

Innanzitutto, esaminiamo il titolo di proprietà predefinito e controlliamo se viene visualizzato correttamente.

it ('controlla il titolo prop di default', () => {
 const SpinnerComponent = mount ();
    prevedono (SpinnerComponent.find ('p'). text ()). toEqual ('Please wait');
});

Quindi controlliamo il titolo di prop personalizzato. Dobbiamo verificare che restituisca l'elica correttamente definita. Dai un'occhiata al codice, il titolo è racchiuso nell'utilità rawMarkup e viene generato con l'aiuto della proprietà dangerouslySetInnerHTML.

Elenco dei codici per rawMarkup util:

esportazione funzione predefinita rawMarkup (modello) {
    return {__html: template};
}

Dobbiamo includere test per rawMarkup nel componente Spinner? No, si tratta di un'utilità separata e deve essere testata a parte lo spinner. Non ci interessa come funziona: dobbiamo solo sapere che il titolo prop restituisce il risultato corretto.

Chiarimento: il motivo dell'utilizzo della proprietà dangerouslySetInnerHTML è il seguente. Il nostro sito è multilingue, di cui è responsabile il team di marketing delle traduzioni. Possono tradurlo semplicemente con una combinazione di parole o persino decorare con i tag HTML, come , , o persino tagliare il testo con le liste

    ,
      . Non sappiamo con certezza come traducono e decorano il testo. Dobbiamo solo rendere correttamente tutte queste cose.

      Ho combinato due casi di test principali in un test:

      • restituisce il titolo prop personalizzato corretto
      • renderizza correttamente il titolo prop con i tag HTML
      it ('controlla il titolo del prop con tag html', () => {
          const props = {
                  titolo: " Attendere "
              },
              SpinnerComponent = mount ();
          prevedono (SpinnerComponent.find ('p'). text ()). toEqual ('Please wait');
      });

      Prendi il sottotitolo prop successivo. È facoltativo ed è per questo che non ha un puntello predefinito, quindi salta il passaggio con i puntelli predefiniti e testa i puntelli personalizzati:

      • Controlla che il testo nel sottotitolo sia visualizzato correttamente:
      const props = {
              sottotitolo: "sinistra 1 minuto"
          },
          SpinnerComponent = mount ();
      it ('render testo corretto', () => {
          si aspettano (SpinnerComponent.find ( 'p') a (1) .text ()). toEqual (props.subTitle).;
      });

      Sappiamo che il sottotitolo è facoltativo. Ecco perché dobbiamo verificare se non è renderizzato con oggetti di scena predefiniti, in base al markup dello slicing. Controlla il numero di tag

      :

      it ('controlla che il sottotitolo non sia reso', () => {
        const SpinnerComponent = mount ();
          si aspettano (SpinnerComponent.find ( 'p') di lunghezza.) .toEqual (1);
      });

      3.Tipi di oggetti di prova:

      • Per la proprietà del titolo dovrebbe essere una stringa:
      it ('controlla il tipo di prop per il titolo è stringa', () => {
          const props = {
                  titolo: "Aspetta"
              },
              SpinnerComponent = mount ();
          si aspettano (SpinnerComponent.find ( 'p') di testo ().) toBeString ().;
      });
      • Per i sottotitoli il puntello dovrebbe essere anche stringa:
      const props = {
              sottotitolo: "sinistra 1 minuto"
          },
          SpinnerComponent = mount ();
      it ('type for subTitle is string', () => {
          si aspettano (SpinnerComponent.find ( 'p') a (1) .text ()). toBeString ().;
      });

      Elenco completo dei test: Spinner.test.js

      4. Test modali (ModalWrapper.js e ModalTrigger.js)

      Sembra:

      Come testare i modali

      Prima di tutto, voglio spiegare come sono organizzati i modali nel nostro progetto. Abbiamo due componenti: ModalWrapper.js e ModalTrigger.js.

      ModalWrapper è responsabile del layout dei popup. Contiene il contenitore modale, il pulsante "chiudi", il titolo e il corpo modali.

      ModalTrigger è responsabile della gestione modale. Include il layout ModalWrapper e contiene eventi per il controllo del layout modale (azioni di apertura e chiusura).

      Esaminerò ciascun componente separatamente:

      1.Codice di codice per il componente testato: ModalWrapper.js

      Facciamo il codice:

      Innanzitutto, ModalWrapper riceve il componente e lo rende al suo interno. Prima di tutto, controlla che ModalWrapper non fallisca senza il componente. Crea uno snapshot con oggetti di scena predefiniti:

      it ('senza componente', () => {
          const ModalWrapperComponent = shallow ();
          si aspettano (ModalWrapperComponent) .toMatchSnapshot ();
      });

      Il prossimo passo è simulare le sue condizioni attuali con il rendering dei componenti passato attraverso i puntelli:

      it ('with component', () => {
         const props = {
                 componente: () => {}
              },
              ModalWrapperComponent = shallow ();
          si aspettano (ModalWrapperComponent) .toMatchSnapshot ();
      });

      Test di oggetti di scena

      Ricezione del nome di classe personalizzato prop:

      it ('render nome classe corretto', () => {
          const props = {
                  modalClassName: 'custom-class-name'
              },
              ModalWrapperComponent = shallow ().find('Modal ');
              si aspettano (ModalWrapperComponent.hasClass ( 'classe-nome personalizzato')) toEqual (true).;
      });

      Ricezione del titolo personalizzato:

      it ('render titolo corretto', () => {
          const props = {
                 titolo: "Titolo modale"
             },
             ModalWrapperComponent = shallow ().find('ModalTitle ');
          prevedono (ModalWrapperComponent.props (). children) .toEqual ('Titolo modale');
      });

      Ricezione puntello spettacolo corretto:

      it ('check value value', () => {
              const props = {
                     mostra: vero
                 },
                 ModalWrapperComponent = shallow ().find('Modal ');
              aspetterebbe (ModalWrapperComponent.props () spettacolo.) .toEqual (true);
          });

      Test dei proptype

      • Per spettacolo prop
      it ('check prop type', () => {
          const props = {
                 mostra: vero
              },
              ModalWrapperComponent = shallow ().find('Modal ');
          aspetterebbe (ModalWrapperComponent.props () spettacolo.) .toBeBoolean ();
      });
      • Per su Prop prop
      it ('render correct onHide prop type', () => {
          const props = {
                  suHide: () => {}
              },
              ModalWrapperComponent = shallow ().find('Modal ');
          si aspettano (ModalWrapperComponent.props () onHide.) .toBeFunction ();
      });
      • Per il componente prop
      it ("render tipo componente prop corretto", () => {
         const props = {
                 componente: () => {}
             },
             ModalWrapperComponent = mount ();
         aspetterebbe (ModalWrapperComponent.props () Component). .toBeFunction ();
      });

      Elenco completo dei test: ModalWrapper.test.js

      2. Elenco di codici per il componente testato: ModalTrigger.js

      Il wrapper modale è stato coperto con un test. La seconda parte è quella di coprire il componente trigger modale.

      Panoramica dei componenti: si basa sullo stato attivato che indica la visibilità di ModalWrapper. Se attivato: false, il popup è nascosto, altrimenti è visibile. La funzione open () apre il popup sull'elemento figlio. L'evento click e la funzione close () nasconde il popup sul pulsante reso in ModalWrapper.

      Creazione di istantanee:

      it ('renderizza correttamente il componente ModalTrigger', () => {
          const ModalTriggerComponent = shallow ( 
      );     si aspettano (ModalTriggerComponent) .toMatchSnapshot (); });

      Dovremmo testare ModalTrigger con il rendering dell'elica componente? No - poiché il componente verrà reso all'interno del componente ModalWrapper. Non dipende dal componente testato. Era già stato coperto con test nei test ModalWrapper.

      Test puntelli:

      Abbiamo un figlio prop e vogliamo essere sicuri di avere un solo figlio.

      it ('assicurarsi di avere un solo figlio (elemento di controllo)', () => {
          explore (ModalTriggerComponent.findWhere (node ​​=> node.key () === 'controllo modale'). lunghezza) .toEqual (1);
      });

      Prove di test:

      L'elica figlio dovrebbe essere un oggetto, quindi controlla questo nel prossimo test:

      const ModalTriggerComponent = mount ( 
      );
      it ('check children prop type', () => {
            si aspettano (ModalTriggerComponent.props () i bambini.) .toBeObject ();
      });

      Una parte importante del componente ModalTrigger è controllare gli stati.

      Abbiamo due stati:

      • Il popup è aperto. Per sapere che il modale è aperto, dobbiamo verificarne lo stato. Per questo, chiama la funzione aperta dall'istanza del componente e aspettati che lo stato attivato / disattivato sia vero.
      it ('controlla che il modale sia aperto', () => {
          const event = {
              preventDefault: () => {},
              stopPropagation: () => {}
          };
          ModalTriggerComponent.instance () aperte (evento).;
          si aspettano (ModalTriggerComponent.state () commutato.) .toBeTruthy ();
      });
      • Il popup è chiuso. È testato viceversa, commutato in stato dovrebbe essere falso.
      it ('controlla che il modale sia chiuso', () => {
         ModalTriggerComponent.instance () close ().;
         si aspettano (ModalTriggerComponent.state () commutato.) .toBeFalsy ();
      });

      Elenco completo dei test: ModalTrigger.test.js

      Ora i modali sono completamente testati. Un consiglio per testare i componenti che dipendono l'uno dall'altro: esaminare prima i componenti e scrivere il piano di test, definire ciò che è necessario testare in ciascun componente, controllare i casi di test per ciascun componente ed assicurarsi di non farlo ripetere lo stesso test case in entrambi i componenti. Analizzare attentamente le varianti possibili e ottimali per la copertura del test.

      5. Test HOC (componente di ordine superiore)

      Le ultime due parti (HOC e test dei campi modulo) sono interconnesse. Vorrei condividere con voi come testare il layout del campo con il suo HOC.

      Ecco una spiegazione di cos'è BaseFieldLayout, perché abbiamo bisogno di questo componente e dove lo utilizziamo:

      • BaseFieldLayout.js è il wrapper per i componenti di input del modulo come TextInput, CheckboxInput, DateInput, SelectInput, ecc. I loro nomi finiscono con -Input perché utilizziamo il pacchetto redux-form e questi componenti sono i componenti di input per la logica redux-form.
      • Abbiamo bisogno di BaseFieldLayout per creare il layout per i componenti dei campi modulo, ovvero il rendering di etichette, descrizioni comandi, prefissi (valuta, abbreviazioni di metri quadrati, ecc.), Icone, errori e così via.
      • Lo usiamo in BaseFieldHOC.js per avvolgere inputComponent nel layout di campo e collegarlo con il modulo redux con l'aiuto del componente .

      Elenco di codici per il componente testato: BaseFieldHOC.js

      È un HOC che riceve il componente input form e restituisce il componente, collegato con redux-form.

      Analizzare il HOC:

      • Questo componente riceve solo un oggetto, componente. Prima di tutto, devo creare questo componente e inserirlo in BaseFieldHOC.
      • Quindi, ho bisogno di decorare il HOC avvolto con redux-form al fine di ottenere il campo collegato con redux-form.
      • Rendi questo campo all'interno del componente di React Redux per rendere il negozio disponibile per il componente testato. Per deridere il negozio, basta fare:
      const store = createStore (() => ({}));

      Ora, prima di ogni test, devo fare quanto segue:

      lasciare BaseFieldHOCComponent;
      beforeEach (() => {
          const TextInput = () => {return 'input di testo'; },
              BaseFieldHOCWrapper = BaseFieldHOC (TextInput),
              TextField = reduxForm ({form: 'testForm'}) (BaseFieldHOCWrapper);
          BaseFieldHOCComponent = renderer.create (
              
                  
              
          ) .ToJSON ();
      });

      Successivamente, il componente è pronto per il test:

      1. Crea istantanea:
      it ('render correttamente componente', () => {
          si aspettano (BaseFieldHOCComponent) .toMatchSnapshot ();
      });

      2. Assicurarsi che il componente di input sia racchiuso in BaseFieldLayout dopo il rendering:

      it ('controlla che il componente di input sia racchiuso in BaseFieldLayout', () => {
          si aspettano (BaseFieldHOCComponent.props.className) .toEqual ( 'forma-gruppo');
      });

      Questo è tutto, l'HOC è coperto. La parte più complicata nel testare componenti collegati con redux-form è preparare il campo (decorare con redux form e setup store). Il resto è facile, basta seguire le istruzioni e nient'altro.

      Elenco completo dei test: BaseFieldHOC.test.js

      6. Test di moduli / campi

      Il campo HOC è coperto da test in modo da poter passare al componente BaseFieldLayout.

      Elenco di codici per il componente testato: BaseFieldLayout.js

      Codifichiamo BaseFieldLayout.js e scriviamo i test secondo le istruzioni sopra:

      1. Prima di tutto, crea un'istantanea.

      Questo componente non verrà reso senza defaultProps:

      • inputComponent
      • Gli oggetti di scena forniti da redux-form: input e meta object. Input con nome proprietà e meta con errore proprietà e toccato:
      const defaultProps = {
         meta: {
              toccato: null,
              errore: null
          },
          input: {
              nome: "nome campo"
          },
          inputComponent: () => {return 'test case'; }
      }

      Per utilizzare defaultProps in ogni wrapper testato, procedi come segue:

      importa TestBaseFieldLayout da '../BaseFieldLayout';
      const BaseFieldLayout = (props) => ;

      Ora siamo pronti per creare un'istantanea:

      it ('renderizza correttamente il componente BaseFieldLayout', () => {
          const BaseFieldLayoutComponent = renderer.create (). toJSON ();
          si aspettano (BaseFieldLayoutComponent) .toMatchSnapshot ();
      });

      2. Puntelli di prova:

      Questo componente ha molti oggetti di scena. Mostrerò alcuni esempi e il resto sarà testato per analogia.

      • Accertarsi che l'icona prop sia rappresentata correttamente
      it ('render correttamente icona prop', () => {
          const props = {
                  icon: 
              },
              BaseFieldLayoutComponent = mount ();
              si aspettano (BaseFieldLayoutComponent.find ( 'arco') hasClass ( 'icona-esclamativo').) toBeTruthy ().;
      });
      • Assicurarsi che il contenuto della descrizione comandi venga visualizzato accanto all'etichetta
      const props = {
              labelTooltipContent: 'descrizione comando per etichetta'
          },
          BaseFieldLayoutComponent = mount ();
      it ('check prop rendering', () => {
         si aspettano (BaseFieldLayoutComponent.find ( 'arco') hasClass ( 'tooltip-icona').) toBeTruthy ().;
      });
      • Campo di prova Link prop
      • Assicurarsi che fieldLink sia null per impostazione predefinita
      it ('check prop è null di default', () => {
          const BaseFieldLayoutComponent = shallow ();
          si aspettano (BaseFieldLayoutComponent.props () fieldLink.) .toBe (null);
      });
      • Assicurarsi che fieldLink venga visualizzato correttamente con un valore personalizzato

      3. Errori di test:

      it ('controlla se il campo ha un errore', () => {
          const props = {
                  meta: {
                      toccato: vero,
                      errore: "Questo campo è obbligatorio"
                  }
              },
              BaseFieldLayoutComponent = mount ();
          si aspettano (BaseFieldLayoutComponent.find () 'errore.') toHaveLength (1).;
      });

      Elenco completo dei test: BaseFieldLayout.test.js

      Linea di fondo

      Ora sai come eseguire test di copertura completa dei componenti in base alla struttura del progetto. Dalla mia esperienza, ho cercato di spiegare cosa è necessario testare, in quale ordine e cosa si può omettere nella copertura del test. Inoltre, ho dimostrato esempi di numerosi componenti di test e ho individuato la sequenza della copertura di codebase.

      Spero che troverai utile questo articolo e condividerai le tue risposte. Grazie per aver letto.

      Se ritieni utile questo post, tocca il pulsante below in basso :)