Tarin Gamberini

A software engineer and a passionate java programmer

Introduzione a Cucumber

Quando un committente (stakeholder) ha una esigenza che vorrebbe fosse soddisfatta da un software deve, in genere, comunicare tale esigenza a qualcuno che lo sappia progettare e sviluppare.

Aldo: “Ci serve un software che faccia questo.”

Barbara: “Sì, a grandi linee mi sembra di aver intuito ciò di cui ha bisogno e penso che si possa fare. Per raccogliere ulteriori informazioni avrei bisogno di sapere con un po' più di dettaglio cosa intende con il termine «questo».”

Aldo: “Bhe, «questo» vuol dire che il software deve portare l'utente lì e là.”

Barbara: “Lì e là sempre?”

Aldo: “No no, deve portare lì quando l'utente si è registrato e poi clicca quiricchicchi, e solo quando l'utente ha nel carrello vari prodotti, e ha nel conto abbastanza lallarallà, deve portarlo là.”

Barbara: “Va bene, e relativamente ai lallarallà come fa il software a sapere quando ne abbiamo «abbastanza»?”

Aldo:

Per ridurre la spiacevole situazione in cui il software sviluppato faccia cose diverse da quelle comunicate i team agili hanno imparato a procedere per piccoli incrementi, al termine di ognuno dei quali si chiede al committente: “È «questo» quello che volevi?”.

Ma che fare quando «questo» non è quello che voleva il committente? Le incomprensioni, si sa, accadono, ma si possono ridurre? Esiste un modo affinché ciò che è stato comunicato guidi ciò che sarà sviluppato?

Sviluppo guidato

Nel mondo Agile si sono affermate metodologie di sviluppo software come:

Con TDD gli sviluppatori usano i test come guida per sviluppare il software. Esistono vari tipologie di test: test di unità, test di integrazione, testi di accettazione, test di carico, ecc… . Una particolare variante dei test di accettazione verifica se ciò che è stato sviluppato dal team sia accettabile per il committente.

BDD fonde TDD con Domain-Driven Design (DDD). Quest'ultimo è un approccio allo sviluppo software che fra i suoi obiettivi ha quello di voler creare una collaborazione fra il committente, che conosce la realtà (domain) che si desidera informatizzare, ed il team di sviluppo, che sa progettare (design) il software. Lo scopo di tale collaborazione è quella di definire un linguaggio condiviso (ubiquitous langauage) che dovrebbe consentire la comunicazione di concetti, anche complessi, riducendo le incomprensioni fra committente e team.

Cucumber

Cucumber svetta sopra altri strumenti simili perché è stato appositamente progettato affinché committenti e team di progettazione e sviluppo siano in grado di leggere e scrivere test di accettazione in modo semplice.

I test di accettazione sono scritti in Gherkin, un Domain Specific Language (DSL) leggibile dal committente (in inglese).

Un esempio di due test di accettazione, scritti da Aldo (committente) e Barbara (team), potrebbe essere:

File: questo.feature
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# language it
Funzionalità: Questo

  Ci serve un software che porti l'utente lì e là.

  Scenario:

    Dato che l'"utente" si è registrato
    Quando l'"utente" clicca "quiricchicchi"
    Allora l'"utente" deve arrivare alla pagina ""

  Scenario:

    Dato che l'"utente" ha nel carrello vari prodotti
    Quando nel conto ha "abbastanza" "lallarallà"
    Allora l'"utente" deve arrivare alla pagina ""

Il file questo.feature è un tipico esempio di documentazione che da un lato descrive ciò che il committente vuole e, dall'altro lato (che vedremo a breve), è collegato al codice sorgente sviluppato.

È importante sottolineare che tale documentazione non potrà mai diventare obsoleta, nel senso che non potrà mai riferirsi a codice sorgente vecchio che non esiste più, perché se fosse obsoleta allora l'esecuzione dei test di accettazione obsoleti fallirebbe nel momento in cui si collega al codice sorgente che esiste ora. In questo senso i .feature file di Cucumber sono considerati documentazione vivente.

Con il passare del tempo i test di accettazione si accumulano e sia il committente che il team continuano a riferirsi a tutti questi .feature file per leggerli, per ragionarci sopra, per comunicare, per modificarli o crearne di nuovi: essi diventano di fatto l'unica sorgente di verità su cosa faccia il software.

Come funziona Cucumber

Cucumber guida il committente ed il team a pensare al proprio software come ad un insieme di funzionalità, ognuna delle quali è documentata in un .feature file.

A questo punto si esce dal territorio comune al committente ed al team e ci si addentra in quello specifico del team di progettazione e sviluppo. Per comprendere il funzionamento di Cucumber ci servono alcune definizioni.

Ogni funzionalità è caratterizzata, in genere, da vari scenari ognuno dei quali è diviso in passi: Dato, Quando, Allora, ecc… Ogni scenario, nello caso del linguaggio Java, è descritto da una classe che ha tanti metodi quanti sono i passi che lo costituiscono:

File: LiSteps.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class LìSteps {
  ...

  public void l_utente_si_e_registrato... {
    ...
  }

  public void l_utente_clicca_quiricchicchi... {
    ...
  }

  public void l_utente_deve_arrivare_alla_pagina... {
    ...
  }
}

Ogni passo é associato al relativo metodo tramite, per esempio, un'appropriata annotazione java (il simbolo @ seguito dal nome del passo) che prende come parametro di input una espressione regolare che “specchia” il testo scritto nel passo. La coppia (annotazione, metodo) è chiamata definizione del passo:

File: LiSteps.java
1
2
3
4
5
6
7
8
  ...

  @Dato("^che l'\"(.*?)\" si è registrato$")
  public void l_utente_si_e_registrato(String idUtente) {
    ...
  }

  ...

Il corpo del metodo costituisce il codice di supporto che implementa, invocando il codice sorgente sviluppato, ciò che è descritto a parole nel passo:

File: LiSteps.java
1
2
3
4
5
6
7
8
9
10
11
  ...

  @Dato("^che l'\"(.*?)\" si è registrato$")
  public void l_utente_si_e_registrato(String idUtente) throws UtenteNonRegistratoException {
    utente = utenteService.findById(idUtente);
    if (utente.isNotRegistrato()) {
        throw new UtenteNonRegistratoException(idUtente);
    }
  }

  ...

Il funzionamento di Cucumber, in breve, può essere descritto come segue.

Cucumber comincia a leggere tutti i .feature file. Per ogni scenario comincia ad eseguire tutte le definizioni dei passi. Per ogni definizione, se eseguita con successo, procede con l'esecuzione della definizione del passo successivo. Se arriva in fondo allo scenario, superando con successo tutti i suoi passi, allora l'intero scenario è marcato con successo. Se lo scenario fallisce Cucumber lo marca come fallito e procede con lo scenario successivo.

Invia un commento

Un commento è inviato attraverso una normalissima e-mail. Il tuo indirizzo e-mail non sarà pubblicato o diffuso.

Questo blog è moderato, per cui alcuni commenti potrebbero non essere pubblicati. I commenti sono solitamente approvati dal moderatore in uno/tre giorni.