Test di Accettazione con Cucumber (LinuxDay 2018 Ferrara)
1.
2. taringamberini.com
Lavora da sempre con tecnologie Java-based Free Software
ed Open Source presso la Regione Emilia-Romagna
È membro attivo del JUG Padova ed ha partecipato al
Tavolo di Lavoro delle Linee Guida dell'Art. 68 del
Codice dell’Amministrazione Digitale
È traduttore presso la Free Software Foundation Europe
Ha creato la lista di parole Diceware in italiano
Quando non cura le sue piantine officinali, si interessa di:
Object Oriented Design, Testing, Build Automation e
Continuous Delivery
5. Alcune Metodologie Agile
(1970 Waterfall Model)
1974 An adaptive software development process documented
1991 Rapid Application Development (RAD)
1995-99 DSDM, SCRUM, XP, FDD
(2001 Agile Manifesto)
2003 Lean Software Development
2006 Kanban
2008 Lean startup
2009 DevOps
6.
7.
8.
9.
10. Alcune Pratiche Agile
(1972 SCCS, 1986 CVS, 2000 SVN, 2005 GIT)
1998 Test Driven Development (TDD)
2003 Acceptance Test Driven Development (ATDD)
2003 Domain Driven Design (DDD)
2004 Behaviour Driven Development (BDD)
2000 Continuous Integration (CI)
2006 Continuous Deployment (CD)
www.agilealliance.org/agile101/practices-timeline
www.agilealliance.org/agile101/agile-glossary
11.
12.
13. Domain Driven Design (DDD)
Image source http://www.guggenheim.org/artwork/1924 - Composition 8 by Vasily Kandinsky
Si distingue per lo sforzo nella definizione di un linguaggio
condiviso, ubiquitous langauage, volto a consentire la
comunicazione di concetti, anche complessi, riducendo le
incomprensioni fra committente (Domain) e team di
sviluppo (Design)
L’ubiquitous langauage è usato in modo pervasivo:
usato da tutti, usato ovunque
discussioni, documentazione, requisiti, progettazione,
architettura, codice sorgente, database, servizi, api, ecc...
18. Test di Accettazione
È un criterio di accettazione espresso come
esempio o scenario di utilizzo
Ha un esito binario:
test superato, test fallito
19. Test di Accettazione
Può essere scritto
(specificato) con
un linguaggio più
o meno formale
È possibile
automatizzarne
l’esecuzione
Per questi due aspetti è anche detto:
specifica eseguibile
(executable specification)
20. Gherkin
È un semplice linguaggio formale per esprimere in
linguaggio (quasi) naturale test di accettazione
Funzionalità,
Contesto, Scenario,
Schema dello scenario, Esempi,
Dato, Data, Dati, Date, Quando, Allora,
E, Ma
# comments, """ doc-strings,
| data-tables, @ tags
Gherkin non è Turing-completo, ha comunque una grammatica con relativo parser
23. Step Definition
Cucumber “capisce” test
di accettazione scritti in
Gherkin
Gli step definition
sono un conveniente
strato di codice
che “aggancia” la
specifica eseguibile
al prodotto software
(glue code)
Prodotto
Step
Definition
Executable
Specification
gherkin
java
Step
Definitions
java
glue
1
2
24. acquisto.feature
# language: it
Funzionalità: Acquisto
Scenario: Comprare una banana
Dato che il prezzo di una banana è 20 centesimi
Quando acquisto 2 banane
Allora il prezzo totale deve essere 40 centesimi
Una specifica eseguibile
è salvata in un file .feature
steps
25. Lanciamo Cucumber
You can implement missing steps with the snippets below:
@Dato("^che il prezzo di una banana è (d+) centesimi$")
public void cheIlPrezzoDiUnaBananaÈCentesimi(int arg1) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@Quando("^acquisto (d+) banane$")
public void acquistoBanane(int arg1) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@Allora("^il prezzo totale deve essere (d+) centesimi$")
public void ilPrezzoTotaleDeveEssereCentesimi(int arg1) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
Tests run: 7, Failures: 0, Errors: 0, Skipped: 4, Time elapsed: 0,344 sec
26. Confronto Feature/Step Definition
Dato che il prezzo di una
banana è 20 centesimi
@Dato("^che il prezzo di una
banana è (d+) centesimi$")
public void cheIlPrezzoDiUna
BananaÈCentesimi(int bananaCentesimi)
throws Throwable {
...
}
Gherkin
Java
Executable Specification
Step Definition
Feature
glue code
27. test: AcquistoStepDefinitions.java
public class AcquistoStepDefinitions {
@Dato("^che il prezzo
di una banana è (d+) centesimi$")
public void cheIlPrezzo
DiUnaBananaÈCentesimi(int bananaCentesimi)
throws Throwable {
// Write code here that turns the
//phrase above into concrete actions
throw new PendingException();
}
...
}
28. Lanciamo Cucumber
Scenario: Comprare una banana #
Dato che il prezzo di una banana è 20 centesimi #
cucumber.api.PendingException: TODO: implement me
Quando acquisto 2 banane #
Allora il prezzo totale deve essere 40 centesimi #
1 Scenarios (1 pending)
3 Steps (2 skipped, 1 pending)
0m0,199s
29. test: AcquistoStepDefinitions.java
public class AcquistoStepDefinitions {
@Dato("^che il prezzo di una banana è (d+) centesimi$")
public void cheIlPrezzoDiUnaBananaÈCentesimi(int bananaCentesimi)
throws Throwable {
int prezzoBanana = bananaCentesimi;
}
@Quando("^acquisto (d+) banane$")
public void acquistoBanane(int numeroBanane)
throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@Allora("^il prezzo totale deve essere (d+) centesimi$")
public void ilPrezzoTotaleDeveEssereCentesimi(int totaleCentesimi)
throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
}
30. Lanciamo Cucumber
Scenario: Comprare una banana #
Dato che il prezzo di una banana è 20 centesimi #
Quando acquisto 2 banane #
cucumber.api.PendingException: TODO: implement me
Allora il prezzo totale deve essere 40 centesimi #
1 Scenarios (1 pending)
3 Steps (1 skipped, 1 pending, 1 passed)
0m0,122s
31. test: AcquistoStepDefinitions.java
public class AcquistoStepDefinitions {
private int prezzoBanana;
@Dato("^che il prezzo di una banana è (d+) centesimi$")
public void cheIlPrezzoDiUnaBananaÈCentesimi(int bananaCentesimi)
throws Throwable {
prezzoBanana = bananaCentesimi;
}
@Quando("^acquisto (d+) banane$")
public void acquistoBanane(int numeroBanane)
throws Throwable {
Carrello carrello = new Carrello();
carrello.acquisto(numeroBanane, prezzoBanana);
}
@Allora("^il prezzo totale deve essere (d+) centesimi$")
public void ilPrezzoTotaleDeveEssereCentesimi(int totaleCentesimi)
throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
}
33. test: AcquistoStepDefinitions.java
public class AcquistoStepDefinitions {
private int prezzoBanana;
@Dato("^che il prezzo di una banana è (d+) centesimi$")
public void cheIlPrezzoDiUnaBananaÈCentesimi(int bananaCentesimi)
throws Throwable {
prezzoBanana = bananaCentesimi;
}
@Quando("^acquisto (d+) banane$")
public void acquistoBanane(int numeroBanane)
throws Throwable {
Carrello carrello = new Carrello();
carrello.acquisto(numeroBanane, prezzoBanana);
}
@Allora("^il prezzo totale deve essere (d+) centesimi$")
public void ilPrezzoTotaleDeveEssereCentesimi(int totaleCentesimi)
throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
}
35. test: AcquistoStepDefinitions.java
public class AcquistoStepDefinitions {
private int prezzoBanana;
@Dato("^che il prezzo di una banana è (d+) centesimi$")
public void cheIlPrezzoDiUnaBananaÈCentesimi(int bananaCentesimi)
throws Throwable {
prezzoBanana = bananaCentesimi;
}
@Quando("^acquisto (d+) banane$")
public void acquistoBanane(int numeroBanane)
throws Throwable {
Carrello carrello = new Carrello();
carrello.acquisto(numeroBanane, prezzoBanana);
}
@Allora("^il prezzo totale deve essere (d+) centesimi$")
public void ilPrezzoTotaleDeveEssereCentesimi(int totaleCentesimi)
throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
}
36. Lanciamo Cucumber
Scenario: Comprare una banana #
Dato che il prezzo di una banana è 20 centesimi #
Quando acquisto 2 banane #
Allora il prezzo totale deve essere 40 centesimi #
cucumber.api.PendingException: TODO: implement me
1 Scenarios (1 pending)
3 Steps (1 pending, 2 passed)
0m0,128s
37. test: AcquistoStepDefinitions.java
public class AcquistoStepDefinitions {
private int prezzoBanana;
private Carrello carrello;
@Dato("^che il prezzo di una banana è (d+) centesimi$")
public void cheIlPrezzoDiUnaBananaÈCentesimi(int bananaCentesimi)
throws Throwable {
prezzoBanana = bananaCentesimi;
}
@Quando("^acquisto (d+) banane$")
public void acquistoBanane(int numeroBanane)
throws Throwable {
carrello = new Carrello();
carrello.acquisto(numeroBanane, prezzoBanana);
}
@Allora("^il prezzo totale deve essere (d+) centesimi$")
public void ilPrezzoTotaleDeveEssereCentesimi(int totaleCentesimi)
throws Throwable {
assertEquals(totaleCentesimi, carrello.getTotaleAcquistato());
}
}
39. test: AcquistoStepDefinitions.java
public class AcquistoStepDefinitions {
private int prezzoBanana;
private Carrello carrello;
@Dato("^che il prezzo di una banana è (d+) centesimi$")
public void cheIlPrezzoDiUnaBananaÈCentesimi(int bananaCentesimi)
throws Throwable {
prezzoBanana = bananaCentesimi;
}
@Quando("^acquisto (d+) banane$")
public void acquistoBanane(int numeroBanane)
throws Throwable {
carrello = new Carrello();
carrello.acquisto(numeroBanane, prezzoBanana);
}
@Allora("^il prezzo totale deve essere (d+) centesimi$")
public void ilPrezzoTotaleDeveEssereCentesimi(int totaleCentesimi)
throws Throwable {
assertEquals(totaleCentesimi, carrello.getTotaleAcquistato());
}
}
40. Lanciamo Cucumber
Scenario: Comprare una banana #
Dato che il prezzo di una banana è 20 centesimi #
Quando acquisto 2 banane #
Allora il prezzo totale deve essere 40 centesimi #
1 Scenarios (1 passed)
3 Steps (3 passed)
0m0,242s
41. acquisto.feature
# language: it
Funzionalità: Acquisto
Scenario: Comprare una banana
Dato che il prezzo di una banana è 20 centesimi
Quando acquisto 2 banane
Allora il prezzo totale deve essere 40 centesimi
Scenario: Comprare una banana
Dato che il prezzo di una banana è 20 centesimi
Quando acquisto 4 banane
Allora il prezzo totale deve essere 80 centesimi
scriviamoli meglio...
42. acquisto.feature
# language: it
Funzionalità: Acquisto
Schema dello scenario: Comprare una banana
Dato che il prezzo di una banana è 20 centesimi
Quando acquisto <numeroBanane> banane
Allora il prezzo totale deve essere <totale> centesimi
Esempi:
| numeroBanane | totale |
| 2 | 40 |
| 4 | 80 |
43. Lanciamo Cucumber
Schema dello scenario: Comprare una banana #
Dato che il prezzo di una banana è 20 centesimi #
Quando acquisto 2 banane #
Allora il prezzo totale deve essere 40 centesimi #
Schema dello scenario: Comprare una banana #
Dato che il prezzo di una banana è 20 centesimi #
Quando acquisto 4 banane #
Allora il prezzo totale deve essere 80 centesimi #
java.lang.AssertionError: expected:<80> but was:<40>
Failed scenarios:
test/acceptance/com/taringamberini/
linuxday2018ferrara/features/acquisto.feature:13 #
2 Scenarios (1 failed, 1 passed)
6 Steps (1 failed, 5 passed)
0m0,130s
44. src: Carrello.java
package com.taringamberini.linuxday2018ferrara;
/**
* @author Tarin Gamberini (www.taringamberini.com)
*/
public class Carrello {
private int totale;
public void acquisto(int numeroBanane, int prezzoBanana) {
totale = numeroBanane * prezzoBanana;
}
public int getTotaleAcquistato() {
// return 40
return totale;
}
}
45. Lanciamo Cucumber
Schema dello scenario: Comprare una banana #
Dato che il prezzo di una banana è 20 centesimi #
Quando acquisto 2 banane #
Allora il prezzo totale deve essere 40 centesimi #
Schema dello scenario: Comprare una banana #
Dato che il prezzo di una banana è 20 centesimi #
Quando acquisto 4 banane #
Allora il prezzo totale deve essere 80 centesimi #
2 Scenarios (2 passed)
6 Steps (6 passed)
0m0,127s
46. Conclusioni
Cucumber automatizza l'esecuzione dei test di accettazione a
partire dalla lettura del comportamento espresso in linguaggio
naturale
Unica sorgente di verità: specifiche, i test e la
documentazione sono nello stesso documento
Documentazione vivente: le nostre specifiche sono sempre
aggiornate perché testate automaticamente da Cucumber
Focalizzato sul committente: Cucumber aiuta le persone
dell’azienda committente e dell’IT a collaborare per costruire una
comprensione condivisa degli obiettivi aziendali