Cominciamo a lavorare
Dopo l’introduzione svolta nel primo articolo della serie, già da questa puntata cominciamo ad affrontare la pratica, presentando un esempio relativo a un ipotetico servizio di eCommerce per cui occorre effettuare un calcolo delle spese di spedizioni. Per svolgere queste operazioni, abbiamo bisogno di alcuni elementi e di un po’ di preparazione, che vediamo subito di seguito.
Repository su GitHub
Per questo progetto abbiamo allestito un repository su GitHub, all’indirizzo che trovate nei riferimenti [1]. Per ogni requisito che andremo a soddisfare creeremo un branch apposito in modo da rendere più semplice possibile allinearsi con i vari articoli della serie.
Nel branch master invece, troverete il progetto vuoto in cui già tutto è configurato, nel caso ci siano problemi seguendo gli step descritti più avanti.
Nel caso non siate avvezzi a Git e al suo funzionamento, vi suggeriamo la serie [2] di articoli scritti da Ferdinando Santacroce e pubblicati in questo periodo su MokaByte su questo sito.
L’esempio: calcolo delle spese di spedizione
Per praticità, abbiamo deciso di usare un caso d’uso che dovrebbe essere abbastanza chiaro a tutti, senza necessità di troppe spiegazioni sulle logiche applicative che vogliamo andare a realizzare per questo progetto.
Parametri in ingresso
Quello che cercheremo di realizzare è un servizio di calcolo delle spese di spedizione da mettere a disposizione del nostro ipotetico eCommerce. Il nostro servizio accetterà in ingresso i seguenti parametri:
- elenco dei prodotti scelti dall’utente contenente il prezzo unitario, il volume (in millimetri), peso (in grammi) e quantità selezionate;
- costo del carrello (quindi eventuali coupon di sconto già applicati);
- destinazione di spedizione;
- corriere (DHL, UPS, FedEx) e relativo servizio (consegna standard o consegna espressa).
Per semplicità assumiamo che tutti i corrieri offrano lo stesso tipo di servizio — anche se sappiamo bene che non è così — e lavoreremo solo sul costo di consegna in base al servizio. Inoltre i valori saranno sempre espressi in euro, le conversioni saranno eventualmente date in pasto a un altro servizio apposito o se ne occuperà l’eCommerce.
Funzionalità
Le funzionalità che si possono realizzare su questo tema sono molteplici e molto complesse, e noi ne abbiamo scelte alcune che ci permettono di introdurvi al TDD senza troppe complicazioni, ma che al contempo vi possono permettere di espanderle e/o aggiungerne di nuove come esercizio personale. In tal caso, vi chiediamo di creare una “pull request” per metterla a disposizione degli altri come confronto.
Regole
Le regole che abbiamo deciso di implementare in questa serie di articoli sono le seguenti:
- Consegna standard: noti il peso volumetrico e il peso totale del carrello, identificare il costo dello specifico corriere. Il costo da utilizzare sarà il più alto tra volumetrico e peso, pertanto ogni corriere avrà una tabella con le fasce di prezzo per peso e volume.
- Spese di spedizione gratuite per valori del carrello superiori a 100,00 euro, indipendentemente dalla destinazione di spedizione.
- Se l’utente sceglie la consegna espressa, dovrà essere applicata una maggiorazione del 30% rispetto al costo risultante dal servizio standard.
- UPS applica una maggiorazione di 3,00 € per le spedizioni in Italia.
- DHL applica una riduzione del 2% per le spedizioni in Brasile.
- FedEx non applica nessuna politica di sconto sulla destinazione.
Le tabelle di calcolo dei corrieri
Le tabelle dei corrieri per pesi e volumi sono riportate di seguito. Per evidenti motivi di semplicità, in questa serie di articoli non implementeremo aspetti reali più complessi, come letture dal DB di tali valori.
Ecco la tabella che riporta i costi (in euro) per i pesi del pacco da spedire (espressi in kg).
da 0 a 2 kg | da 2 a 5 kg | sopra i 5 kg | |
DHL | 5,00 | 10,00 | 20,00 |
UPS | 6,50 | 9,00 | 21,00 |
FedEx | 7,00 | 12,00 | 24,00 |
C’è poi la tabella dei valori volumetrici. Per il calcolo volumetrico ci baseremo sul rapporto 1:200, secondo cui si ipotizza che ogni metro cubo da spedire pesi 200 kg. Anche questa è una semplificazione utile allo scopo dei nostri articoli perché nella realtà ogni corriere applica il suo rapporto: per esempio, BRT usa il rapporto 1:300.
Per capirsi, un pacco da 1000 mm × 1000 mm × 1000 mm = 1.000.000.000 mm cubici / 200 = 5.000.000. Con un esempio relativo a un pacchetto da 30 × 25 × 10 cm, i volumi che arriveranno al servizio dovranno subire la seguente formula per ottenere il peso volumetrico: 300 mm × 250 mm × 100 mm / 5.000.000 = 1,5 kg volumetrici. Con queste premesse, ecco la tabella dei costi in euro relativi ai pesi volumetrici.
da 0 a 3 kg vol | da 3 a 7 kg vol | sopra i 7 kg vol | |
DHL | 5,50 | 8,00 | 27,00 |
UPS | 6,00 | 11,00 | 30,00 |
FedEx | 5,00 | 10,00 | 29,00 |
Ora che abbiamo descritto cosa andremo a creare, procediamo alla preparazione del nostro progetto.
Preparare l’ambiente di lavoro
Come con ogni progetto, preparare l’ambiente di lavoro è un passo fondamentale per poter svolgere al meglio tutto quello che ci aspetta. Per questo progetto useremo:
- Visual Studio Code: a nostro parere un ottimo editor;
- NodeJS: scriveremo il codice in JavaScript;
- Mocha e Chai: le suite per i test
Procediamo quindi a scaricare e installare Visual Studio Code dal sito ufficiale [3]. Per l’installazione non ci sono particolari configurazioni da impostare, pertanto procedete pure con le impostazioni standard.
NodeJS [4] si scarica selezionando la versione “Recommended For Most User”.
Primi passi
A questo punto creiamo una cartella per il progetto chiamata “shippingservice” e apriamo Visual Studio Code. Dal menu “file” clicchiamo sulla voce “apri cartella” e selezioniamo la cartella creata in precedenza.
Premiamo Ctrl + ò per aprire un terminale e digitiamo il comando “npm init”. Il comando provvederà a creare il file di configurazione del progetto ponendoci alcune domande:
- Name: lasciamo invariato
- Version: lasciamo invariato
- Description: la descrizione che preferite
- Entry point: lasciamo invariato
- Test command: mocha ./tests/**/*.js
- Git repository: lasciamo vuoto o mettete il vostro repository, se ne avete creato uno
- Keywords: lasciamo vuoto
- Author: lasciamo vuoto o mettete il nome che preferite
- License: lasciamo invariato
- Tutto OK, premiamo invio per completare l’operazione
In figura 1, uno screenshot di quanto appena descritto.
Per procedere all’installazione dei rimanenti pacchetti, dal terminale di Visual Studio Code lanciamo i seguenti comandi:
npm install mocha chai --save-dev
Procediamo ora a creare due cartelle nel nostro progetto denominate “src” e “tests”. Con questo abbiamo preparato il nostro progetto con i componenti di base per cominciare a fare TDD.
Checkout dal branch master
Se avete optato per la scelta di fare il checkout del branch master, anziché seguire passo-passo la preparazione dell’ambiente, dopo aver aperto il progetto in Visual Studio Code, aprite un terminale (Ctrl+ò) ed eseguite il comando:
npm install
Questo comando provvede a scaricare e installare tutti i plugin necessari e dovrete solo creare le due sottocartelle “tests” e “src”.
La scrittura del primo test
Come prima cosa, una breve introduzione alla nomenclatura che abbiamo deciso di adottare. Nella cartella src collocheremo il codice del programma, mentre nella cartella tests, come si evince dal nome, i test. I file che creeremo in tests avranno lo stesso nome del file contenente le funzioni da testare e con il suffisso .tests. Per fare un esempio pratico se in src creiamo il file shippingService.js in tests creeremo il file shippingService.tests.js che conterrà i test relativi.
Come spiegato nel primo articolo di questa serie, il TDD prevede prima la scrittura del test e poi la scrittura del codice che lo soddisfa; pertanto cominceremo definendo un semplice test il cui scopo è verificare che ci siano tutti i parametri necessari per il calcolo delle spese di spedizione, vale a dire queli che abbiamo descritto più sopra in questo articolo.
Creiamo quindi nella cartella tests il file shippingService.tests.js e scriviamo il seguente codice:
const expect = require('chai').expect; const ShippingService = require('../src/shippingService'); describe('ShippingService Tests', () => { it('should returns false if no parameters are passed', () => { const result = ShippingService.getShippingFees(); expect(result).to.have.property('requestValid', false); expect(result).to.have.property('shippingFees', 0); expect(result).to.have.property('message', 'Specificare i parametri della richiesta'); }); });
Test fallito… ovviamente
Apriamo il terminale (Ctrl+ò) ed eseguiamo:
npm test
Otterremo il primo rendiconto dei test che segnalano un errore in quanto non viene trovato il file shippingService.js. Ci troviamo quindi all’inizio del ciclo red–green–light spiegato il mese precedente. Prima di procedere a far diventare “verde” il test spieghiamo brevemente cosa abbiamo scritto.
La prima riga attiva le funzioni di Chai per eseguire i test, mentre la seconda istanzia l’oggetto ShippingService — abbiamo fondamentalmente definito come si chiamerà il nostro file con il servizio — che vogliamo testare.
Le righe successive eseguono il primo semplicissimo test; si verifica cioè che, se non viene passato alcun parametro al nostro servizio, il risultato deve essere il nostro oggetto contenente quanto spiegato in precedenza.
Soddisfare il test: primo codice
Ora cominciamo a soddisfare il test, pertanto creiamo il file shippingService.js nella cartella src del nostro progetto e creiamo la funzione chiamata dal test denominata getShippingFees.
module.exports = { getShippingFees(params) { if (typeof params === 'undefined') { return { requestValid: false, shippingFees: 0, message: 'Specificare i parametri della richiesta' }; } return null; },};
Se ora lanciamo il comando di test dovremmo essere “verdi” (figura 2).
Ulteriori passi avanti
Ora sempre seguendo il pattern red–green–light–refactor, scriviamo gli altri semplici test che si assicurano che i parametri siano validi, cominciando dai prodotti.
it('should returns false if products are not valid', () => { const params = {}; const result = ShippingService.getShippingFees(params); expect(result).to.have.property('requestValid', false); expect(result).to.have.property('shippingFees', 0); expect(result).to.have.property('message', 'Specificare l\’elenco dei prodotti'); });
Soddisfiamo il test integrando il nostro servizio con i debiti controlli e risposte.
if (typeof params.products === 'undefined' || params.products.length === 0) { return { requestValid: false, shippingFees: 0, message: 'Specificare l\’elenco dei prodotti' }; }
Procediamo adesso a verificare che torni errore se non abbiamo specificato il corriere. La cosa che sappiamo a priori è che dovremo fornire una lista valida di prodotti affinché passi il primo controllo che abbiamo scritto in precedenza:
it('should returns false if courier is empty', () => { const params = { products: [ { code: 'ART1', quantity: 2, pricePerUnit: 34.5, volume: 6000000, weight: 250 }, { code: 'ART2', quantity: 1, pricePerUnit: 89.90, volume: 40000000, weight: 450 }, ], }; const result = ShippingService.getShippingFees(params); expect(result).to.have.property('requestValid', false); expect(result).to.have.property('shippingFees', 0); expect(result).to.have.property('message', 'Specificare il corriere'); });
Eseguendo npm test avremo il nuovo test rosso e pertanto implementiamo il codice per soddisfarlo.
if (typeof params.courier === 'undefined' || params.courier === '') { return { requestValid: false, shippingFees: 0, message: 'Specificare il corriere' }; }
I rimanenti controlli, per ora, sono identici e vi invitiamo a implementarli in autonomia. Si possono ovviamente scrivere molteplici test, ipotizzando una qualche validazione sul tipo di corriere e servizio, oppure un controllo più approfondito della struttura dei prodotti o addirittura una gestione delle risposte sulla base della lingua passata insieme ai parametri.
L’ultimo test… per questa volta
Come ultimo test per questa puntata cominciamo a ragionare sul risultato che dobbiamo ottenere nel caso i dati passati siano validi. Come prima cosa, ci assicuriamo che requestValid sia true e il messaggio sia vuoto, mentre il calcolo del valore lo demanderemo a test più specifici nel prossimo articolo.
it('should returns true if valid params are passed', () => { const params = { products: [ { code: 'ART1', quantity: 2, pricePerUnit: 34.5, volume: 6000000, weight: 250 }, { code: 'ART2', quantity: 1, pricePerUnit: 89.90, volume: 40000000, weight: 450 }, ], courier: 'DHL', courierService: 'Standard', destination: 'ITA', }; const result = ShippingService.getShippingFees(params); expect(result).to.have.property('requestValid', true); expect(result).to.have.property('message').to.be.empty; });
Il codice che soddisfa il test per ora è semplicissimo in quanto non eseguiamo ancora alcun calcolo:
return { requestValid: true, shippingFees: 0, message: '' };
Conclusioni
In questo numero abbiamo cominciato a fare pratica con il TDD, creando l’ambiente di lavoro e iniziando il nostro percorso di test con una applicazione per il calcolo delle spese di spedizione di un ipotetico eCommerce.
Il mese prossimo passeremo al testing delle funzioni di calcolo effettivo dei costi di spedizione. Per adesso, però dovreste aver capito con test banali l’approccio TDD. Ricordo che stiamo testando tutte le singole condizioni per scopo dimostrativo, starà poi a voi con l’esperienza capire cosa è effettivamente importante testare e cosa no.
Come ultima cosa, se doveste riscontrare problemi nel realizzare quanto descritto finora, trovate tutti i test nel branch 01_service_parameters sul repository dedicato al progetto [1].