• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content

Loading…

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

Like this presentation? Why not share!

Introduzione al TDD

on

  • 1,338 views

Introduzione al TDD con esempi e consigli.

Introduzione al TDD con esempi e consigli.

Statistics

Views

Total Views
1,338
Views on SlideShare
1,329
Embed Views
9

Actions

Likes
3
Downloads
5
Comments
0

2 Embeds 9

http://www.linkedin.com 7
https://www.linkedin.com 2

Accessibility

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • Questa presentazione è una breve introduzione al Test Driven Development. Questa è una presentazione rivolta a chi: - ha sentito parlare della tecnica - che non l’ha ancora provata o ha appena iniziato - e che vorrebbe saperne qualcosa di più. Non c’è nessun argomento avanzato, solo un assaggio. Gli obiettivi della presentazione sono: spiegare di cosa si tratta fornirvi un idea di quello che permette di fare spiegare i vantaggi dell’approcio Non è una trattazione completa dell’argomento. Per riuscire ad usarla con profitto sarebbe necessario almeno leggere alcuni libri che eventualmente dopo indicherò oppure meglio ancora lavorare con qualcuno bravo che la usa.
  • Questi sono I contenuti della presenazione. Partiremo dallo spiegare brevemente i concetti che sono dietro alla tecnica. Esporremo la meccanica alla base della del processo. Infine vedremo due esemp. Un esempio sullo sviluppo in TDD di un parser. Un esempio tratto dal un mio recente lavoro.
  • Io ho individuato due concetti alla base del TDD. Test First Il primo è scrivere il test prima. Cioe’ scriverlo prima che il codice di produzione sia creato. Questo perche’ l’altra strategia, il test after, non funziona. O almeno funziona male. I problemi con il test after sono che: - alla fine il test non viene scritto - testare del codice che non e’ pensato per essere testato puo’ essere veramente molto difficile Invece con il test first si riesce a definire a priori sia l’interfaccia del codice da sviluppare sia il comportamento che il codice deve avere. Automate your tests L’altro aspetto fondamentale è che tutti i test vengono automatizzati. Il motivo è semplice testare a mano è noioso. Ricompilare, avviare il programma, inserire gli input necessari per attivare quel particolare pezzo di codice che stiamo scrivendo è lungo e tedioso. Scrivere test automatici richiede un certo tempo, ma dopo aver fatto un po’ di pratica con gli strumenti il tempo è di meno di quello necessario per scrivere il test automatico.
  • L’interfaccia si evolve  La GET() non era adatta per gestire sia testo che gli mp3 Nasce il concetto di Resource L’interfaccia si evolve per supportare le nuove funzionalità senza rovinare il design
  • Nota: refactoring anche dei test scritti precedentemente che saranno adattati alla nuova API.
  • Nella slide è riportato un esempio di test. Questo esempio di test riguarda lo sviluppo di un applicazione che gestisce il cestino di Linux. Quando in buttiamo un file nel cestino il sistema si deve ricordare alcuni metadati come il path da cui il file e’ stato “rimosso” e la data in cui questa cosa e’ successa. In Linux queste informazioni sono memorizzate in un file di testo che ha una sintassi simile alla stringa content che vedete nelle slide. Questo è un test per il metodo parse() della classe TrashInfo e verifica che sia in grado di leggere il contenuto del file passato in input. La caratteristica importante di questi test è che il controllo dell’esito è automatico, in altre parole che non é necessaria l’ispezione o il controllo da parte dello sviluppatore. L’altro aspetto importante é l’esecuzione é molto veloce perché non ci sono operazioni di I/O. Questo tipo di test è chiamato test di unità.

Introduzione al TDD Introduzione al TDD Presentation Transcript

  • Introduzione al Test Driven Development Andrea Francia http://www.andreafrancia.it
  • Contents
    • Concepts
    • Definition
    • Example1: Parser
    • Example2: Text To Speech
  • Concepts
    • Test First
    • Automate tests
  • What is a Automated Test?
  • Automated Test Lifecycle Giordano Scalzo: http://www.slideshare.net/giordano/tdd-iphonefordummies
  • Automated Test Lifecycle Giordano Scalzo: http://www.slideshare.net/giordano/tdd-iphonefordummies
  • Automated Test Lifecycle SUT: System Under Test Giordano Scalzo: http://www.slideshare.net/giordano/tdd-iphonefordummies
  • Automated Test Lifecycle SUT: System Under Test Giordano Scalzo: http://www.slideshare.net/giordano/tdd-iphonefordummies
  • Automated Test Lifecycle SUT: System Under Test Giordano Scalzo: http://www.slideshare.net/giordano/tdd-iphonefordummies
  • Example of a Manual Test: nextLine()
    • public static void main (String[] args) {
    • String text = "first line second line" ;
    • Scanner scanner = new Scanner(text);
    • System.out.println(scanner.nextLine()); // prints "first line"
    • System.out.println(scanner.nextLine()); // prints "second line”
    • }
    Code:
    • assertEquals(expected, actual);
    PASS FAIL expected == actual yes no
  • Using JUnit
    • @Test public void howNextLineWorks() throws IOException {
    • String text = "first line second line" ;
    • Scanner scanner = new Scanner(text);
    • assertEquals ( ”first line" , scanner.nextLine());
    • assertEquals ( ”second line" , scanner.nextLine());
    • }
    Code: Output:
  • What is Test Driven Development?
  • What is TDD?
    • Test-driven development ( TDD ) is a software development process that relies on the repetition of a very short development cycle:
    Giordano Scalzo: http://www.slideshare.net/giordano/tdd-iphonefordummies Tim Ottinger: http://agileinaflash.blogspot.com/2009/02/red-green-refactor.html
  • RED
    • first the developer writes a failing automated test case that defines a desired new behaviour (of the software),
  • GREEN
    • then produces code to pass that test …
  • Refactor
    • and finally refactors the new code to acceptable standards.
  •  
  • Example: Writing “df” Parser
  • The df output
    • [andreafrancia@deneb Dropbox]$ df
    • Filesystem 1K-blocks Used Available Use% Mounted on
    • /dev/disk0s2 243862672 135971832 107634840 56% /
    • /dev/disk0s1 243862672 135971832 107634840 56% /tmp
    • /dev/disk1s2 243862672 135971832 107634840 56% /opt
    • devfs 109 109 0 100% /dev
    Mount Points
  • The problem
    • List all mount points
  • The Problem
    • Write a method that extract the list of the mount points from the output of df.
  • First of all decompose the problem!
  • Decomposition
    • Parse the output of “df” process …
      • When there are no mounted volumes
      • When there is only one volume mounted
      • When there are many volumes
      • When the a volume contains whitespaces
  • First Cycle
    • Parse the output of “df” process
      • When there are no volumes
      • When there is only one volume
      • When there are many volumes
      • When the a volume contains whitespaces
    • @Test
    • public void whenNoVolumes () {
    • String input = "Filesystem 1024-blocks Used Available Capacity Mounted on” ;
    • List<String> result = parseDfOutput (input);
    • assertEquals(emptyList(), result);
    • }
  •  
    • @Test
    • public void whenNoVolumes () {
    • String input = &quot;Filesystem 1024-blocks Used Available Capacity Mounted on” ;
    • List<String> result = parseDfOutput(input);
    • assertEquals(emptyList(), result);
    • }
    • private List<String> parseDfOutput (String string) {
    • return null ;
    • }
    • @Test
    • public void whenNoVolumes () {
    • String input = &quot;Filesystem 1024-blocks Used Available Capacity Mounted on” ;
    • List<String> result = parseDfOutput(input);
    • assertEquals(emptyList(), result);
    • }
    • private List<String> parseDfOutput (String string) {
    • return emptyList();
    • }
    • No need for refactoring
  • Second Cycle
    • Parse the output of “df” process
      • When there are no volumes
      • When there is only one volume
      • When there are many volumes
      • When the a volume contains whitespaces
    • @Test public void whenOneVolume () {
    • List<String> result = parseDfOutput( &quot;&quot;
    • + &quot;Filesystem 1024-blocks Used Available Capacity Mounted on &quot;
    • + &quot;/dev/disk0s2 243862672 135479924 108126748 56% /&quot; );
    • assertEquals(asList( &quot;/&quot; ), result);
    • }
    • private List<String> parseDfOutput (String string) {
    • return emptyList();
    • }
    • private List<String> parseDfOutput (String string) {
    • List<String> result = new ArrayList<String>();
    • return result;
    • }
    • private List<String> parseDfOutput (String input) {
    • List<String> result = new ArrayList<String>();
    • Scanner scanner = new Scanner(input);
    • scanner.nextLine(); // skip header
    • if (scanner.hasNextLine()) {
    • String line = scanner.nextLine();
    • Scanner scanner1 = new Scanner(line);
    • scanner1.next(); // skip Filesystem
    • scanner1.next(); // skip 1024-blocks
    • scanner1.next(); // skip Used
    • scanner1.next(); // skip Available
    • scanner1.next(); // skip Capacity
    • String mountPoint = scanner1.next();
    • result.add(mountPoint);
    • }
    • return result;
    • }
    • private List<String> parseDfOutput (String input) {
    • List<String> result = new ArrayList<String>();
    • Scanner lines = new Scanner(input);
    • lines .nextLine(); // skip header
    • if ( lines .hasNextLine()) {
    • String line = lines .nextLine();
    • Scanner values = new Scanner(line);
    • values .next(); // skip Filesystem
    • values .next(); // skip 1024-blocks
    • values .next(); // skip Used
    • values .next(); // skip Available
    • values .next(); // skip Capacity
    • String mountPoint = values .next();
    • result.add(mountPoint);
    • }
    • return result;
    • }
    • private List<String> parseDfOutput(String input) {
    • List<String> result = new ArrayList<String>();
    • Scanner lines = new Scanner(input);
    • lines.nextLine(); // skip header
    • if (lines.hasNextLine()) {
    • result.add(parseMountPoint(lines.nextLine()));
    • }
    • return result;
    • }
    • private String parseMountPoint(String line) {
    • Scanner values = new Scanner(line);
    • values.next(); // skip Filesystem
    • values.next(); // skip 1024-blocks
    • values.next(); // skip Used
    • values.next(); // skip Available
    • values.next(); // skip Capacity
    • String mountPoint = values.next();
    • return mountPoint;
    • }
  • Third Cycle
    • Parse the output of “df” process
      • When there are no volumes
      • When there is only one volume
      • When there are many volumes
      • When the a volume contains whitespaces
    • @Test public void whenMultipleVolume () {
    • List<String> result = parseDfOutput( &quot;&quot;
    • + &quot;Filesystem 1024-blocks Used Available Capacity Mounted on &quot;
    • + &quot;/dev/disk0s2 243862672 135479924 108126748 56% / &quot;
    • + &quot;/dev/disk0s2 243862672 135479924 108126748 56% /media/disk &quot;
    • + &quot;/dev/disk0s2 243862672 135479924 108126748 56% /tmp &quot; );
    • assertEquals(asList( &quot;/&quot; , &quot;/media/disk&quot; , &quot;/tmp&quot; ), result);
    • }
    • @Test public void whenMultipleVolume () {
    • List<String> result = parseDfOutput( &quot;&quot;
    • + &quot;Filesystem 1024-blocks Used Available Capacity Mounted on &quot;
    • + &quot;/dev/disk0s2 243862672 135479924 108126748 56% / &quot;
    • + &quot;/dev/disk0s2 243862672 135479924 108126748 56% /media/disk &quot;
    • + &quot;/dev/disk0s2 243862672 135479924 108126748 56% /tmp &quot; );
    • assertEquals(asList( &quot;/&quot; , &quot;/media/disk&quot; , &quot;/tmp&quot; ), result);
    • }
    • private List<String> parseDfOutput (String input) {
    • List<String> result = new ArrayList<String>();
    • Scanner lines = new Scanner(input);
    • lines.nextLine(); // skip header
    • if (lines.hasNextLine()) {
    • String line = lines.nextLine();
    • String mountPoint = parseMountPoint(line);
    • result.add(mountPoint);
    • }
    • return result;
    • }
    • private List<String> parseDfOutput (String input) {
    • List<String> result = new ArrayList<String>();
    • Scanner lines = new Scanner(input);
    • lines.nextLine(); // skip header
    • while (lines.hasNextLine()) {
    • String line = lines.nextLine();
    • String mountPoint = parseMountPoint(line);
    • result.add(mountPoint);
    • }
    • return result;
    • }
  • TDD Rules
  • TDD Rules
    • Test First
    • Test for All Features
    • Remove all duplication (always)
  • Some hints
    • Keep tests running and passing
    • Keeps test small
    • Don’t mix phases (Red, Green, Refactor)
    • Don’t mix unit tests with integration tests
    • Test only one feature per test
  • Examples from my last work
  • Regole applicate
    • Ogni feature deve essere sviluppata secondo il TDD
    • Partire dai test di accettazione
    • Ritardare le decisioni di design all’ultimo momento responsabile
    • Applicare un principio di design solo dopo aver avuto la prova che sia utile in quel specifico caso
  • Prima feature: produrre testo pronunciabile
  • Test di accettazione
    • public class HoroscopeTest {
    • @Test public void
    • userListenOroscope() {
    • Horoscope horoscope = new Horoscope();
    • horoscope.saveDivination( &quot;22-GIU-10&quot; , &quot;Ariete&quot; ,
    • &quot;Sarai molto fortunato.&quot; );
    • String result = horoscope.GET( &quot;/horoscope/ariete.txt&quot; );
    • assertEquals( &quot;22 giugno 2010: Ariete, Sarai molto fortunato.” , result);
    • }
  • No test  No code
    • Quando scrivo il codice di produzione scrivo il minimo necessario a far passare il test
    • Se il minimo non mi convince (è troppo stupido), vuol dire che manca una specifica funzionale  cioè manca un test.
    • Prima di scrivere una qualsiasi riga di codice in più aggiungo un test che la richieda.
    • @Test public void
    • shouldStoreDivinationsForMultipleSigns() {
    • Horoscope horoscope = new Horoscope();
    • horoscope.saveDivination( &quot;22-GIU-10&quot; , &quot;Ariete&quot; ,
    • &quot;for ariete&quot; );
    • horoscope.saveDivination( &quot;22-GIU-10&quot; , &quot;Toro&quot; ,
    • &quot;for toro&quot; );
    • assertEquals( &quot;22 giugno 2010: Ariete, for ariete&quot; ,
    • horoscope.GET( &quot;/horoscope/ariete.txt&quot; ));
    • assertEquals( &quot;22 giugno 2010: Toro, for toro&quot; ,
    • horoscope.GET( &quot;/horoscope/toro.txt&quot; ));
    • }
  • Cerco di non anticipare il design
    • Prima di affrontare lo sviluppo faccio una veloce sessione di design
    • Non implemento nessuna decisione fino a che non si rende necessaria
    • E.g. anche servirà un DAO per adesso salvo tutto in RAM
  • Seconda Feature: oroscopo ascoltabile
    • @Test public void howToCreateMp3() {
    • Horoscope horoscope = new Horoscope(
    • aFakeSyntetizerWhichReturns(
    • aMp3Stream()));
    • horoscope.saveDivination( &quot;22-GIU-10&quot; , &quot;Ariete&quot; ,
    • &quot;divination&quot; );
    • assertThat(horoscope.GET(
    • &quot;/horoscope/ariete.mp3&quot; ). asByteArray() ,
    • is(equalTo(aMp3Stream())));
    • }
  • Resource
    • Ora il GET restituisce una Resource
      • public Resource GET(String path) {...}
    • Il client decide quale rappresentazione usare:
    • horoscope.GET( &quot;/horoscope/ariete/divination.txt&quot; ).asString());
    • horoscope.GET( &quot;/horoscope/ariete/divination.mp3&quot; ).asByteArray();
  • Terza feature: la persistenza
  • Stato delle cose
    • Al momento tutto viene persistito in memoria (in una HashMap)
    • Non esiste ancora un oggetto DAO, tutto viene fatto dall’unica class Horoscope
  • Estrazione del comportamento
    • public class MemoryDivinationRepo {
    • Divination lastDivinationForSign(String sign);
    • void saveDivinationFromPronounce(String sign,
    • String pronounce);
    • };
    • public interface Divination {
    • String asText();
    • byte [] asMp3();
    • };
  • Estrazione dell’interfaccia
    • public class MemoryDivinationRepo
    • implements DivinationRepo {...}
    • public interface DivinationRepo {
    • Divination lastDivinationForSign(String sign);
    • void saveDivinationFromPronounce(String sign,
    • String pronounce);
    • };
  • Caratterizzazione del comportamento
    • L’interfaccia è una minima parte del contratto , la parte più importante è il comportamento che l’oggetto dovrebbe avere.
    • Il comportamento lo estraggo con dei test di caratterizzazione
  • Caratterizzazione di MemoryDivinationRepo
    • public class MemoryDivinationRepoTest {
    • @ Test public void
    • shouldStoreDivinationPronounceForASign() {...}
    • @ Test public void
    • shouldReplyWithAPronouncedMp3() {...}
    • @ Test public void
    • shouldStoreDivinationPronounceForMultipleSigns() {...}
    • @ Test public void
    • shouldOverrideDivinationPronounce() {...}
    • }
  • Thanks
    • Andrea Francia
    • www.andreafrancia.it
    • blog.andreafrancia.it
    • [email_address]
  • Extra slides
  • Example of an Automated Test
    • @Test public void shouldParsePath() {
    • String content =
    • &quot;[Trash Info] &quot;
    • + &quot;Path=/home/andrea/foo.txt &quot;
    • + &quot;DeletionDate=2010-08-23T12:59:14 &quot; ;
    • String path = Parser . parsePath(content);
    • assertEquals( &quot;/home/andrea/foo.txt” , path);
    • }
  • Testing Frameworks http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks
    • PHPUnit
    • SimpleTest
    • Google C++ Testing Framework
    • CppUnitLite
    • CppUnit
    • JUnit
    • TestNG
    • PyUnit
    • Nose