Se vuoi scoprire come testare i componenti React con Enzyme e Jest sei nel posto giusto. In questa guida ti insegnerò a scrivere i test dei componenti, mentre la scorsa volta abbiamo visto insieme come scrivere un test JavaScript (scoprilo a questo indirizzo).
Questo articolo può essere letto e capito da chiunque conosca già ReactJS, ma fa parte di una guida molto più ampia che ti insegna ReactJS (puoi trovarla quì).
Come testare i componenti React
Scrivere i test è qualcosa di veramente molto importante. Come già ti ho raccontato spesso i test non solo assicurano che alcune righe di codice funzionano, ma nella realtà dei fatti i pregi dello scrivere i test sono molti di più.
In rete e tra alcuni programmatori ogni tanto qualcuno inizia a parlare di quelli che io chiamo “falsi difetti” e tra questi il più comune è la “perdita di tempo”. Nella realtà dei fatti scrivere i test ti permette di:
- Scovare bug rapidamente con un’attività di debug molto più rapida
- Scrivere codice pulito
- Scrivere metodi e classi con poche righe di codice
- Favorire il riutilizzo
- Favorire l’utilizzo di interfacce
- Definire più rapidamente ed efficientemente l’architettura del software
- Rendere il codice più parlante evitando così i commenti (che spesso non vengono mai aggiornati)
- Descrivere il comportamento di un metodo/funzione in modo che tutto il team possa capirlo (a differenza dei commenti dove alcune volte vengono usate parole che possono essere interpretate in maniera differente)
Ci sono questi e tanti altri motivi. Per chi ancora pensa che scrivere i commenti sia la soluzione migliore per avere un codice ben documentato e per chi crede che scrivere i test rallenti la produzione (una volta che impari a scrivere i test il tuo codice risulterà funzionante in molto meno tempo, fidati!) allora consiglio qualche libro interessante:
Non essere spaventato dalla data dei libri. Questi sono eterni 🙂
Come testare i componenti React?
La premessa era di dovere, ora vediamo come testare i componenti React.
Per prima cosa partirò, seguendo la guida, dal codice che abbiamo scritto fino ad oggi. Puoi scaricarlo velocemente da quì o seguire la guida per imparare a farlo da solo.
Cos’è Jest?
Jest è un framework per i test JavaScript utilizzato e creato da Facebook (si potrebbe dire che è una vera e propria piattaforma per i test) e che sta diventando il punto di riferimento per i test, specialmente quando si parla di React.
Non a caso online se guardi come testare i componenti React viene subito fuori Jest. Jest ormai viene integrato con il progetto create-react-app ed oltre tutto è utilizzato tutti i giorni da chi ha dato vita a ReactJS (ovvero Facebook!).
Non necessita di configurazione, funziona anche con TypeScript e con React Native, e puoi anche provarlo direttamente sul sito web di riferimento.
Cos’è Enzyme?
Enzyme è un utility JavaScript per testare i componenti ReactJS rendendo più facile la manipolazione dei component, così come la verifica degli assert che possiamo fare sui componenti.
Puoi scoprire di più direttamente sul loro repository.
Come testare i componenti React
Bene, ci siamo! Adesso vediamo un po’ di codice.
Per prima cosa è necessario che ti posizioni con il terminale nella tua applicazione React e che installi le dipendenze per “enzyme” e “react-test-renderer”:
npm install --save enzyme react-test-renderer
Non so quando leggerai questo articolo, ma se hai problemi con react-test-renderer oggi (12 Dicembre 2017) il problema potrebbe essere dovuto al fatto che se controlli nel file package.json hai una versione di react-test-renderer superiore alla 15.
Quindi se stai usando React 15 (nel mio caso 15.6.1) modifica il package.json impostando come versione di react-test-renderer la versione 15.6.1. Dunque dal terminale lancia “npm update”.
Una volta fatto ciò puoi installare anche “enzyme-adapter-react-15”:
npm install --save enzyme-adapter-react-15
Il Setup
Per poter iniziare a scoprire come testare i componenti React è necessario anche fare un’altra piccola cosa per il setup.
All’interno della cartella “src” del tuo progetto inserisci il file setupTests.js che andrà a configurare l’adapter per Enzyme. Scrivi al suo interno:
import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-15'; configure({ adapter: new Adapter() });
Test di CustomInput
Per imparare come testare i componenti React partiamo dal test di un componente che non fa altro che predisporre un input customizzato e utilizzato in tutta la web app.
Per chi ha seguito la guida si trova all’interno della cartella “common/input” e non è altro che un banale input che può ricevere come props la funzione per gestire onChange, una label e poco altro.
Entra nella cartella “test” e crea al suo interno la cartella “common” che a sua volta avrà all’interno la cartella “input”.
Crea all’interno di quest’ultima cartella il file “customInput.test.js” ed al suo interno inserisci questo codice:
import React from 'react'; import CustomInput from "../../../common/input/customInput"; import { shallow } from 'enzyme'; it('CustomInput change text is called', () => { let onChangeFunctionSpy = jest.fn(); const customInputComponent = shallow(<CustomInput onChange={onChangeFunctionSpy}/>); customInputComponent.find('input').simulate('change'); expect(onChangeFunctionSpy).toHaveBeenCalled(); }); it('CustomInput has label', () => { let onChangeFunctionSpy = jest.fn(); const customInputComponent = shallow(<CustomInput label={'test'} onChange={onChangeFunctionSpy}/>); const label = <div className = "input-group-addon"> test </div>; expect(customInputComponent.contains(label)).toEqual(true); }); it('CustomInput has not label', () => { let onChangeFunctionSpy = jest.fn(); const customInputComponent = shallow(<CustomInput onChange={onChangeFunctionSpy}/>); expect(customInputComponent.find('.input-group-addon').length).toEqual(0); });
Stiamo importando React, poi il componente che vogliamo testare ed infine la funzione “shallow” di Enzyme. Shallow è una funzione che ci permette di simulare il rendering di un componente e di poter chiamare su di lui alcuni metodi molto utili.
Cosa stiamo facendo in ordine nei 3 tests?
- Il primo test serve per verificare che al cambiamento del contenuto nell’input realmente viene chiamata la funzione passata a CustomInput come props.
Il test crea tramite “jest.fn()” una funzione che è in realtà un mock, quindi è farlocca. Dunque viene renderizzato con shallow il componente CustomInput. Grazie ad Enzyme ed a Shallow utilizziamo il metodo “find” per trovare l’input (possiamo usare anche classi, .class, o id, #id) e simuliamo l’onChange. Infine ci aspettiamo che la funzione farlocca passata a CustomInput sia stata quindi chiamata. - Il secondo test vuole testare che il componente ha la label. CustomInput ha la label solo se gli viene passata tra le props.
Quindi per prima cosa renderizziamo il componente. Dunque inseriamo in una costante il codice HTML che ci aspettiamo di trovare visto che abbiamo passato nelle props la label con valore “test”. Infine verifichiamo che il nostro componente contenga il codice relativo alla label. - Il terzo test verifica che non venga trovato il <div> contenente la label perché non passiamo nessuna label tra le props.
Verifica il funzionamento dei test eseguendo da terminale “npm test” o “npm run test”.
Test di Login
A questo punto sai come testare i componenti React. Prima vediamo qualcosa di leggermente più difficile e poi ti lascio andare 🙂
Crea sempre nella cartella “test” una cartella “components” ed al suo interno una cartella “login”. Dunque crea dentro a “login” il file “login.test.js”.
Come ti ricorderai il login mostra messaggi di successo e di errore, ma principalmente al click sul pulsante di login esegue una chiamata HTTP con Axios. Per fare ciò si nutriva del LoginService che non faceva altro che chiamare un endpoint e, una volta ottenuto il risultato, restituire la risposta chiamando la callback onSuccess o onError.
Dai test però diventa spesso complicato testare le chiamate HTTP ed inoltre questo deve pretendere che il server debba per forza funzionare. Noi invece vogliamo fare test generici e non coinvolgere il server al momento.
Quindi per prima cosa crea a fianco del file “login.test.js” il file “fakeLoginService.js” che conterrà:
export default class FakeLoginService{ login(email, password, onSuccess, onError){ onSuccess({username:"test", password:"test", token: 'testtoken'}); } }
Quello che abbiamo fatto è creare una classe farlocca che rispetta la stessa interfaccia del vero LoginService e che risponderà sempre al login chiamando la callback di onSuccess con un utente di test.
Vediamo adesso quale sarà il codice all’interno di “login.test.js”:
import React from 'react'; import Login from '../../../components/login/login'; import { shallow } from 'enzyme'; import FakeLoginService from "./fakeLoginService"; it('Login has send button', () => { const loginComponent = shallow(<Login />); const primaryButton = <button type="submit" className = "btn btn-primary pull-right" onClick={loginComponent.instance().login}> Invio </button>; expect(loginComponent.contains(primaryButton)).toEqual(true); }); it('CustomInput login is clicked', () => { const loginComponent = shallow(<Login />); loginComponent.instance().loginService = new FakeLoginService(); loginComponent.find('.btn-primary').simulate('click'); const successMessageDiv = <div style={{color:"green"}}> Complimenti per il login, il tuo token è: testtoken </div>; expect(loginComponent.state('showSuccess')).toEqual(true); expect(loginComponent.state('successMessage')).toEqual('Complimenti per il login, il tuo token è: testtoken'); expect(loginComponent.contains(successMessageDiv)).toEqual(true); }); it('Login has success message', () => { const loginComponent = shallow(<Login />); loginComponent.setState({showSuccess:true, successMessage:"Success!"}); const successMessageDiv = <div style={{color:"green"}}> Success! </div>; expect(loginComponent.contains(successMessageDiv)).toEqual(true); }); it('Login has not success message', () => { const loginComponent = shallow(<Login />); const successMessageDiv = <div style={{color:"green"}}> Success! </div>; expect(loginComponent.contains(successMessageDiv)).toEqual(false); }); it('Login has error message', () => { const loginComponent = shallow(<Login />); loginComponent.setState({showError:true, errorMessage:"Error!"}); const errorMessageDiv = <div style={{color:"red"}}> Error! </div>; expect(loginComponent.contains(errorMessageDiv)).toEqual(true); });
Quello che stiamo facendo è importare React, il componente di Login, la funzione Shallow ed il FakeLoginService.
Dunque i test in ordine contengono:
- Il primo test ormai lo conosciamo e controlla che il componente contiene il pulsante per l’invio del login.
Come puoi vedere all’evento onClick sta passando la stessa funzione di login contenuta nell’istanza del componente Login. - Il secondo test osserva il comportamento al click del pulsante di invio del login.
In questo caso andiamo a simulare il render del nostro componente. Dunque modifichiamo l’istanza di LoginService contenuta nel componente andando ad assegnargli il nostro FakeLoginService. Questo è necessario perché altrimenti il test scoppierebbe chiamando il LoginService reale (esegue reali chiamate HTTP).
Preciso che il LoginService viene istanziato nel costruttore del componente Login nella forma “this.loginService = new LoginService()”.
Dunque troviamo il pulsante con classe “btn-primary” e simuliamo il click. Creiamo una costante contenente il codice che ci aspettiamo dal messaggio di successo.
Infine verifichiamo che lo state del componente contenga nella proprietà showSuccess il valore true e in successMessage il messaggio definito dal componente. Per concludere verifichiamo proprio la presenza “fisica” del div contenente il messaggio di successo del login. - I test successivi sono banali ormai. Unica cosa da notare è la possibilità di andare a cambiare lo state di un componente (in questo caso chiamiamo “loginComponent.setState({showSuccess:true, successMessage:”Success!”})”.
Se non hai chiuso il terminale prima o non hai usato la shortcut CTRL + C allora mentre scrivi il codice i test si auto-aggiornano ed eseguono continuamente.
Conclusioni
In questo articolo hai imparato come testare i componenti React con Enzyme e Jest. Questo è solo l’inizio, ma devo dire che la cultura dei test in Italia è sempre stata un po’ debole. Nella realtà dei fatti ci stiamo muovendo molto bene per quanto riguarda i test unitari con Java ed altri linguaggi prettamente backend e ad oggetti. Mentre per i linguaggi funzionali o più orientati al frontend siamo ancora un po’ indietro.
Per approfondire meglio tutto questo ti consiglio seriamente di leggere i libri che ti ho consigliato sopra ed anche di farti una cultura più ampia leggendo: I 10 + 1 Libri che dovrebbe leggere ogni programmatore.
Molto utili sono anche i corsi che ho selezionato su Udemy, puoi leggere l’articolo quì.
Oltre ai libri che ti ho consigliato sopra, ti consiglio anche un libro specifico per JavaScript: JavaScript Unit Testing.
Iscriviti alla newsletter e non ti perderai i nuovi articoli. Mando da 1 a 4 mail al mese ricapitolando dove siamo arrivati con ogni guida. Con la newsletter hai anche accesso a libri ed ebook gratuiti, così come a coupon Udemy ? .
Per dubbi o domande scrivimi nei commenti ? .
Se ti è piaciuto l’articolo seguimi su Facebook e Twitter oppure rimani sempre aggiornato con la newsletter (da 1 a 4 mail al mese!).