Linting e test end-to-end Cypress in AWS Amplify
Di rientro dalla pausa estiva, ho ripreso l’approfondimento di AWS Amplify. In un precedente articolo avevo trattato la realizzazione di una semplice web application utilizzando questo framework di AWS. In questo post voglio invece trattare l’implementazione di linter per il codice sorgente e dei test end-to-end con Cypress, ovviamente automatizzati nella pipeline CI/CD di AWS Amplify.
Questo il link al repository GitHub e questo il link alla web application del progetto.
Linter
Il linter è uno strumento che analizza il codice sorgente per contrassegnare errori di programmazione, bug, errori stilistici e costrutti sospetti.
(wikipedia)
Utilizzare uno o più strumenti linter ci consente di aumentare la qualità del codice sorgente prodotto. Utilizzare tali strumenti in una pipeline di CI/CD ci permette inoltre di effettuare il deployment di un’applicazione solo quando il codice sorgente della stessa rispetta determinati livelli qualitativi.
La web application che ho realizzato prevede un frontend React e un backend lambda Python. Avremo quindi bisogno due differenti strumenti, specifici per le due tecnologie utilizzate.
ESLint
Uno dei migliore linter che ho avuto la possibilità di testare per JavaScript è ESLint: le sue funzionalità permettono non solo di testare ma anche di porre rimedio (fix automatico) ad un elevato numero di problemi.
Ho utilizzato quindi ESLint per verificare il codice del frontend. La sua installazione è semplice: dalla directory principale del nostro progetto, possiamo installare il linter con il seguente comando.
npm install eslint --save-dev
Terminata l’installazione, il wizard per la prima configurazione si avvia in questo modo.
npx eslint --init
Verranno richieste diverse informazioni: in generale è possibile scegliere se utilizzare ESLint per:
- la verifica della sintassi
- la verifica della sintassi e la ricerca di problemi
- la verifica della sintassi, la ricerca di problemi e il rispetto stilistico nella scrittura del codice
Quest’ultima opzione è interessante e consente di “aderire” ad uno stile tra i più popolari, come quello scelto da Airbnb o da Google. Terminata la configurazione, verranno installati i packages richiesti. Nel mio caso:
"devDependencies": {
"eslint": "^7.8.1",
"eslint-config-google": "^0.14.0",
"eslint-plugin-react": "^7.20.6"
}
Ora che abbiamo configurato ESLint, procediamo alla verifica del codice:
npx eslint src/*.js
A seconda delle opzioni scelte durante la configurazione, il risultato potrebbe non essere dei più ottimisti, rilevando una lunga serie di problemi.
Come però dicevo inizialmente, ESLint ci consente di correggere automaticamente alcuni di questi, utilizzando il seguente comando:
npx eslint src/*.js --fix
Ottimo! Degli 87 problemi rilevati inizialmente, solo 6 richiedono il nostro intervento per essere corretti.
Possiamo inoltre decide di ignorare alcuni problemi specifici. Volendo, per esempio, evitare la segnalazione per l’assenza dei commenti JSDoc, si deve modificare la sezione rules del file .eslintrc.js di configurazione di ESLint.
'rules': {
"require-jsdoc": "off"
},
Rimane solo un errore, relativo ad una linea troppo lunga e che andremo direttamente a correggere.
Abbiamo svolto manualmente l’operazione di linting del codice. L’obiettivo prefissato è inoltre automatizzare questo processo nella pipeline di CI/CD, in modo da interrompere il deployment nel caso alcuni errori vengano rilevati.
Configuriamo la pipeline modificando il file amplify.yml in questo modo:
frontend:
phases:
preBuild:
commands:
- npm ci
build:
commands:
- npx eslint src/*.js
- npm run build
artifacts:
baseDirectory: build
files:
- '**/*'
cache:
paths:
- node_modules/**/*
Tra i comandi della fase di build del frontend, inseriamo il comando per eseguire ESLint. Qualora lo stesso rilevi un problema, la pipeline si interromperà automaticamente.
Per quanto riguardo il frontend è tutto! Per eventuali approfondimenti nell’uso di ESLint, consiglio di consultare l’ottima documentazione.
Pylint
E’ arrivato il momento di verificare il codice di backend. Avendo realizzato alcune funzioni AWS Lambda in Python, ho scelto Pylint per questo scopo. L’installazione si effettua normalmente con PIP:
pip install pylint
Per analizzare un file con Pylint basta eseguirlo specificando il nome del file come argomento.
A differenza del tool visto in precedenza, Pylint attribuisce un punteggio al codice analizzato. Il punteggio si basa ovviamente sui problemi rilevati.
Alcuni problemi possono essere ignorati andando a modificare il file di configurazione. Per generare un file di configurazione da personalizzare successivamente con le proprio impostazioni, è necessario eseguire il comando:
pylint --generate-rcfile
Il file generato è ben documentato e comprensibile.
Al fine di integrare Pylint nella pipeline di AWS Amplify, le operazioni da compiere sono più complicate di quanto visto in precedenza per ESLint.
Per prima cosa è necessario prevedere l’installazione di Pylint nell’immagine Docker utilizzata per la fase di build del backend. Come spiegato nel precedente articolo che ho scritto a riguardo di AWS Amplify, l’immagine Docker di default utilizzata per la build della soluzione non è correttamente configurata: esiste infatti un problema noto relativo alla configurazione di Python 3.8 riportato anche in questa issue (Amplify can’t find Python3.8 on build phase of CI/CD).
Per ovviare al problema, il workaround più semplice che ho individuato è stato creare un’immagine Docker con tutti i requisiti necessari. Di seguito il relativo Dockerfile.
Installato Pylint, andremo a configurare la pipeline modificando il file amplify.yml in questo modo:
backend:
phases:
build:
commands:
- '# Evaluate backend Python code quality'
- find amplify/backend/function -name index.py -type f | xargs pylint --fail-under=5
- '# Execute Amplify CLI with the helper script'
- amplifyPush --simple
Utilizziamo find per sottoporre a Pylint tutti i file index.py delle nostre lambda function di backend. Particolare attenzione deve essere posta al parametro –fail-under: questo istruisce Pylint per considerare fallita una valutazione del codice sorgente inferiore a 5. In questo caso l’exit code di Pylint sarà diverso da zero e quindi interromperà la pipeline di CI/CD. Con una valutazione superiore a 5, l’exit code sarà invece pari a zero. La soglia di default è 10, corrispondente ad un codice sorgente “perfetto” e onestamente molto difficile da ottenere in presenza di applicazioni complesse.
Abbiamo completato l’introduzione dei linter nella nostra pipeline. Vediamo ora come automatizzare il test della nostra applicazione.
End-to-end test con Cypress
Cypress è una soluzione che permette di effettuare test automatici della nostra web application, simulando le operazioni che un utente esegue tramite UI. I test sono raccolti in suite.
Il superamento dei test garantisce il corretto funzionamento dal punto di vista dell’utente.
La console Amplify offre un’integrazione con Cypress (un framework di test end-to-end) per test basati su browser. Per le app Web che utilizzano Cypress, la console Amplify rileva automaticamente i comandi di test sulla connessione repo, esegue i test e fornisce accesso a video, schermate e qualsiasi altro artefatto reso disponibile durante la fase di compilazione.
(AWS)
L’installazione è molto semplice:
npm install cypress
Con il seguente comando avviamo per la prima Cypress:
npx cypress open
Questo comporterà la creazione di una directory cypress nel nostro progetto che include tutti i file di configurazione e le test suite. Alcune di esempio sono presenti in cypress/integration. Non mi addentro nei dettagli di come realizzare una test suite perché esiste già un’immensa documentazione a riguardo.
Configurazione di Cypress per AWS Amplify e Cognito
Vediamo come configurare correttamente il nostro progetto per integrarsi con AWS Amplify.
Il primo file che andremo a modificare è cypress.json presente ora nella root del progetto.
{
"baseUrl": "http://localhost:3000/",
"experimentalShadowDomSupport": true
}
Il primo parametro indica l’URL base della nostra web application. Il secondo parametro experimentalShadowDomSupport è invece molto importante nel caso di applicazioni React che utilizzando il backend di autenticazione Cognito di AWS Amplify e i relativi componenti UI. In estrema sintesi, non abilitando il supporto Shadow DOM, non saremo in grado di autenticarci nella nostra applicazione durante le fasi di testing.
La test suite utilizzata per verificare le funzionalità di login e logout della web application è il seguente:
Per motivi di sicurezza username e password da utilizzare per i test non sono specificate nello script ma andranno definite in variabili di ambiente direttamente sulla console di AWS Amplify. Le variabili sono CYPRESS_username e CYPRESS_password.
Configurazione della Pipeline
Non resta che configurare la pipeline per l’esecuzione dei test, così come indicato da AWS. Ed ecco il file amplify.yml al completo.
Anche in questo caso, come per i linter, la nostra pipeline di deployment si interromperà se i test non avranno esito positivo.
E’ possibile vedere un filmato relativo all’esecuzione dei vari test direttamente dalla console di AWS Amplify, effettuando il download degli artefatti. Di seguito un esempio.
Conclusioni
L’utilizzo combinato di linter e tool di test end-to-end ci consente di rilasciare codice di qualità e verificato dal punto di vista dell’utilizzatore finale. Con AWS Amplify abbiamo la possibilità di integrare questi tool semplificando le operazioni di DevOps e mantenendo un controllo puntuale delle fasi di CI/CD.
In conclusione confermo le impressioni del primo post: la prossima volta che dovrò realizzare una soluzione, che sia un semplice PoC o una web application più complessa, prenderò sicuramente in considerazione l’utilizzo di AWS Amplify! Ovviamente affiancato da Cypress e da uno o più linter.
Ci siamo divertiti? Alla prossima!