SlideShare a Scribd company logo
1 of 29
Download to read offline
The Hitchhiker's
            Guide to
          testable code



semplici regole per scrivere codice semplice da testare
                                  Davide Cerbo - davidecerbo@gmail.com - JUG Roma
                                       Nicola Raglia - n.raglia@gmail.com - JUG Roma
Non parleremo di...

   – XP Programming

– Test-Driven Development

         – Agile

        – Scrum

        – etc etc
...ma parleremo di...

                 come scrivere codice
                   TESTABILE
perchè l'unico modo per applicare le metodologie dette in
                precedenza è scrivere i
                 TEST UNITARI
     e l'unico modo per scriverli è produrre codice
                   TESTABILE
Definizioni assortite

Test: processo atto ad individuare carenze funzionali e non
funzionali durante la fase di sviluppo del software.

Test Unitario: è un test atto a verificare una componente
elementare del software possibilmente in termini di
isolamento dalle dipendenze

Refactoring: è il processo che prevede una ristrutturazione
del codice modificando il meno possibile le interfacce
Ancora definizioni assortite

Design Pattern: soluzione progettuale generale a un
problema ricorrente

Mock Object: oggetti destinati a simulare il comportamento
di oggetti reali.
Durante il test con i mock object abbiamo:
    o creazione mock
    o definizione del comportamento del mock object
    o esecuzione del test
    o verifica del comportamento del mock object
Esempio di codice brutto
Iniziamo dal costruttore

public RubricaImpl(Properties properties, ApplicationContext applicationContext) {
    this.user = applicationContext.getAuthenticationContext().getUser();
    this.url = properties.getProperty("url");
    this.userName = properties.getProperty("userName");
    this.password = properties.getProperty("password");
    try {
       this.connection = DriverManager.getConnection(url, userName, password);
    } catch (SQLException e) {
       //gestione eccezione
    }
    this.database = new DatabaseImpl(connection);
  }
Il nostro primo (non) Unit Test
public void testConstructor() throws Exception {
    Properties properties = new Properties();
    properties.load(new FileInputStream("database.properties"));
    ApplicationContext applicationContext = ApplicationContext.getContext();
    Rubrica rubrica = new RubricaImpl(properties, applicationContext);
 }

con i Mock Objects:
public void testConstructor() throws Exception {
    Properties properties = new Properties();
    properties.setProperty("user", "dbuser");
    properties.setProperty("password","dbpassword");
    properties.setProperty("url", "jdbc:db:///test");
    ApplicationContext applicationContext = createMock(ApplicationContext.class);
    AuthenticationContext authContext = createMock(AuthenticationContext.class);
    expect(applicationContext.getAuthenticationContext()).andReturn(authContext);
    expect(authContext.getUser()).andReturn(createMock(User.class));
    replay(authContext, applicationContext);
    Rubrica rubrica = new RubricaImpl(properties, applicationContext);
    verify(authContext, applicationContext);
  }
Rispettiamo la legge

public RubricaImpl(String url, String userName, String password, User user) {
   this.user = user;
   this.url = url;
   this.userName = userName;
   this.password = password;
   Connection connection=DriverManager.getConnection(url,userName,password);
   this.database = new DatabaseImpl(connection);
 }


     Per rispettare la legge di Demeter un oggetto può solo invocare i metodi:
 •    propri
 •    dei suoi parametri
 •    di ogni oggetto che crea
 •    delle sue variabili
Mai dire CONTEXT
public RubricaImpl(Properties properties, ApplicationContext applicationContext) {
    this.user = applicationContext.getUser();
    this.url = properties.getProperty("url");
    .......
    .......
}

public RubricaImpl(String url, String userName, String password, User user) {
    this.user = user;
    this.url = url;
    .......
    .......
}

    applicationContext e properties sono oggetti         di contesto
    quindi difficilmente testabili unitariamente e richiedono
    fatica aggiuntiva nel test con i mock object.
Vietato affaticare
public RubricaImpl(String url, String userName, String password, User user) {
    .....
   this.userName = userName;
    Connection connection = DriverManager.getConnection(url,userName,password);
   this.database = new DatabaseImpl(connection);
}

public RubricaImpl(String url, String userName, String password, User user) {
   .....
   this.userName = userName;
   this.database = DatabaseManager.getDatabase(url,userName,password);
 }




   Questa è una soluzione ma non va bene perché si usa un
                         metodo statico
Solo l'indispensabile
public RubricaImpl(String url, String userName, String password, User user) {
    this.userName =userName;
    this.database = DatabaseManager.getDatabase(url,userName,password);
  }




             2 SOLUZIONI DA EVITARE!!!
public RubricaImpl(User user) {
    this.user = user;
    this.database = DatabaseSingleton.getInstance();
  }


                     Ecco fatta un po' di pulizia!
     Non era giusto far conoscere alla Rubrica le informazioni per
                        accedere al database!
         Ma è spuntato un singleton e questo è male!
Dependency Injection

public RubricaImpl(Database database, User user) {
    this.user = user;
    this.database = database;
  }




               Il costruttore è stato alleggerito da
                   responsabilità non proprie.
                       Ma ora come lo usiamo?
Pattern Abstract Factory

public class RubricaFactoryImpl implements RubricaFactory {
  private final DatabaseFactory databaseFactory;

    public RubricaFactoryImpl(DatabaseFactory databaseFactory) {
      this.databaseFactory = databaseFactory;
    }

    public Rubrica getRubrica(User user) {
      return new RubricaImpl(databaseFactory.getDatabase(), user);
    }

}

       La responsabilità di creare oggetti sarà sempre data
           ad una Factory o ad altri pattern creazionali.
Passiamo al Database
public class DatabaseFactoryImpl implements DataBaseFactory {
  private final Properties properties;

    public DatabaseFactoryImpl(Properties properties) {
      this.properties = properties;
    }
    public Database getDatabase(){
      String url = properties.getProperty("url");
      String userName = properties.getProperty("userName");
      String password = properties.getProperty("password");
      Connection connection = null;
      try {
         connection = DriverManager.getConnection(url, userName, password);
      } catch (SQLException e) { //gestione eccezione }
      return new DatabaseImpl(connection);
    }
}

                DatabaseFactoryImpl non è testabile,
               andrebbe fatto ulteriore refactoring, ma il
                           tempo è poco :(
Il Test (quasi) finale (1/2)‫‏‬

public void testConstructor() throws Exception {
    Database database = createMock(Database.class);
    User user = createMock(User.class);
    replay(database, user);
    Rubrica rubrica = new RubricaImpl(database, user);
    verify(database, user);
}




         Non c'è bisogno di descrivere comportamento per gli
         oggetti mock perchè il costruttore non fa niente altro
                        che costruire l'oggetto.
                    Ma le factory appena create?
Il Test (quasi) finale (2/2)‫‏‬

public void testFactory() throws Exception {
    DatabaseFactory databaseFactory = createMock(DatabaseFactory.class);
    Database database = createMock(Database.class);
    User user = createMock(User.class);
    expect(databaseFactory.getDatabase()).andReturn(database);
    replay(databaseFactory, user, database);
    RubricaFactory rubricaFactory = new RubricaFactoryImpl(databaseFactory);
    Rubrica rubrica = rubricaFactory.getRubrica(user);
    verify(databaseFactory, user, database);
    assertNotNull(rubrica);
  }
Gli obbiettivi raggiunti

Single responsability
Assegnare la giusta responsabilità
Utilizzare la Dependency Injection
Dividere il fare dal creare
Evitare stati globali
Design by Interface
Andiamo avanti...

public void publish(){
  Context context = new InitialContext();
  Object reference = context.lookup("PublisherService");
  PublisherEjb home = (PublishEjb)PortableRemoteObject.narrow(reference,PublishEjb.class);
  PublisherService publisher = home.create();
  publisher.publish(this);
}
Testiamolo...

Totalmente non testabile in termini
            unitari!!!
Via il Sevice Locator

public RubricaImpl(Database database, User user, PublisherService publisher) {
    this.user = user;
    this.database = database;
    this.publisher = publisher;
}


public void publish(){
     this.publisher.publish(this);
}



         Iniettiamo una classe che abbia la responsabilità di
         pubblicare. Nel nostro caso lo farà tramite EJB, ma sarà
         semplice sostituire la tecnologia.
Ancora non è finita...

public RubricaImpl(Database database, User user) {
    this.user = user;
    this.database = database;
  }


public void publishWith(PublisherService publisher){
  publisher.publish(this);
}



         Passare l'oggetto PublisherService al costruttore è errato
         perché non è necessario al normale ciclo di vita della
         Rubrica, ma serve solo nel caso di una richiesta di
         pubblicazione
Problema solo spostato

Abbiamo solo spostato il problema, infatti
l'implementazione PublisherServiceEJB sarà
intestabile unitariamente...


      ...ma fortunatamente la nuova specifica EJB
      3.0 ci viene in aiuto eliminando il
      ServiceLocator


Ma non è lo scopo di questo talk spiegare
come :D
Il Test finale

public void testPublish() throws Exception {
    Database database = createMock(Database.class);
    User user = createMock(User.class);
    replay(database, user);
    Rubrica rubrica = new RubricaImpl(database, user);
    verify(database, user);

      PublisherService publisherService = createMock(PublisherService.class);
      publisherService.publish(rubrica);
      replay(publisherService, user);
      rubrica.publishWith(publisherService);
      verify(publisherService, user);
  }
Bibliografia
Google Testing Blog
http://googletesting.blogspot.com/

Refactoring: Improving the Design of Existing Code (Martin Fowler)‫‏‬
http://www.refactoring.com/

Refactoring Workbook (William C. Wake)‫‏‬
http://xp123.com/rwb/

Applicare UML e Pattern (Craig Larman)‫‏‬
http://www.craiglarman.com

Principi di ingegneria del software (Pressman)‫‏‬
http://highered.mcgraw-hill.com/sites/0072853182/

Wikipedia
http://www.wikipedia.org
Strumenti utili
Unit Test:
www.junit.org

Test code coverage:
http://cobertura.sourceforge.net/
http://emma.sourceforge.net/

Testability:
http://code.google.com/p/testability-explorer/
http://testabilityexplorer.org/report

Mock objects:
http://www.easymock.org/
http://www.jmock.org/
http://code.google.com/p/mockito/
I nostri contatti

Davide Cerbo
davidecerbo@gmail.com
davide.cerbo@pro-netics.com
http://jesty.it

Nicola Raglia
n.raglia@gmail.com
nicola.raglia@pro-netics.com
http://sourcengineering.org
Q&A




Q&A
Q&A

More Related Content

Similar to The Hitchhiker's Guide to testable code: semplici regole per scrivere codice semplice da testare

Introduzione a Node.js
Introduzione a Node.jsIntroduzione a Node.js
Introduzione a Node.jsMichele Capra
 
E suap - tecnologie client
E suap - tecnologie client E suap - tecnologie client
E suap - tecnologie client Sabino Labarile
 
Presentazione Testing automatizzato
Presentazione Testing automatizzatoPresentazione Testing automatizzato
Presentazione Testing automatizzatoangelolu
 
jQuery - 1 | WebMaster & WebDesigner
jQuery - 1 | WebMaster & WebDesignerjQuery - 1 | WebMaster & WebDesigner
jQuery - 1 | WebMaster & WebDesignerMatteo Magni
 
TDD in WordPress
TDD in WordPressTDD in WordPress
TDD in WordPresslucatume
 
Corso pratico di C# - 2013
Corso pratico di C# - 2013Corso pratico di C# - 2013
Corso pratico di C# - 2013Matteo Valoriani
 
jQuery - 1 | WebMaster & WebDesigner
jQuery - 1 | WebMaster & WebDesignerjQuery - 1 | WebMaster & WebDesigner
jQuery - 1 | WebMaster & WebDesignerMatteo Magni
 
Entity Framework 4 vs NHibernate 3
Entity Framework 4 vs NHibernate 3Entity Framework 4 vs NHibernate 3
Entity Framework 4 vs NHibernate 3Martino Bordin
 
Programmazione a oggetti tramite la macchina del caffé (pt. 2)
Programmazione a oggetti tramite la macchina del caffé (pt. 2)Programmazione a oggetti tramite la macchina del caffé (pt. 2)
Programmazione a oggetti tramite la macchina del caffé (pt. 2)Marcello Missiroli
 
Come portare il profiler di symfony2 in drupal8
Come portare il profiler di symfony2 in drupal8Come portare il profiler di symfony2 in drupal8
Come portare il profiler di symfony2 in drupal8Luca Lusso
 
Corso Object Oriented Analysis and Design
Corso Object Oriented Analysis and DesignCorso Object Oriented Analysis and Design
Corso Object Oriented Analysis and DesignK-Tech Formazione
 
Asp.Net MVC 3 - Il Model View Controller secondo Microsoft
Asp.Net MVC 3 - Il Model View Controller secondo MicrosoftAsp.Net MVC 3 - Il Model View Controller secondo Microsoft
Asp.Net MVC 3 - Il Model View Controller secondo MicrosoftStefano Benedetti
 
Android Test Driven Development
Android Test Driven DevelopmentAndroid Test Driven Development
Android Test Driven Developmentsazilla
 
Android Test Driven Development
Android Test Driven DevelopmentAndroid Test Driven Development
Android Test Driven Developmentsazilla
 
Dominare il codice legacy
Dominare il codice legacyDominare il codice legacy
Dominare il codice legacyTommaso Torti
 

Similar to The Hitchhiker's Guide to testable code: semplici regole per scrivere codice semplice da testare (20)

Introduzione a Node.js
Introduzione a Node.jsIntroduzione a Node.js
Introduzione a Node.js
 
E suap - tecnologie client
E suap - tecnologie client E suap - tecnologie client
E suap - tecnologie client
 
Spring 2.5
Spring 2.5Spring 2.5
Spring 2.5
 
Spring Intro
Spring IntroSpring Intro
Spring Intro
 
Corso Java 2 - AVANZATO
Corso Java 2 - AVANZATOCorso Java 2 - AVANZATO
Corso Java 2 - AVANZATO
 
Presentazione Testing automatizzato
Presentazione Testing automatizzatoPresentazione Testing automatizzato
Presentazione Testing automatizzato
 
jQuery - 1 | WebMaster & WebDesigner
jQuery - 1 | WebMaster & WebDesignerjQuery - 1 | WebMaster & WebDesigner
jQuery - 1 | WebMaster & WebDesigner
 
TDD in WordPress
TDD in WordPressTDD in WordPress
TDD in WordPress
 
Il web 2.0
Il web 2.0Il web 2.0
Il web 2.0
 
Corso pratico di C# - 2013
Corso pratico di C# - 2013Corso pratico di C# - 2013
Corso pratico di C# - 2013
 
jQuery - 1 | WebMaster & WebDesigner
jQuery - 1 | WebMaster & WebDesignerjQuery - 1 | WebMaster & WebDesigner
jQuery - 1 | WebMaster & WebDesigner
 
Entity Framework 4 vs NHibernate 3
Entity Framework 4 vs NHibernate 3Entity Framework 4 vs NHibernate 3
Entity Framework 4 vs NHibernate 3
 
Programmazione a oggetti tramite la macchina del caffé (pt. 2)
Programmazione a oggetti tramite la macchina del caffé (pt. 2)Programmazione a oggetti tramite la macchina del caffé (pt. 2)
Programmazione a oggetti tramite la macchina del caffé (pt. 2)
 
Come portare il profiler di symfony2 in drupal8
Come portare il profiler di symfony2 in drupal8Come portare il profiler di symfony2 in drupal8
Come portare il profiler di symfony2 in drupal8
 
Corso Object Oriented Analysis and Design
Corso Object Oriented Analysis and DesignCorso Object Oriented Analysis and Design
Corso Object Oriented Analysis and Design
 
Asp.Net MVC 3 - Il Model View Controller secondo Microsoft
Asp.Net MVC 3 - Il Model View Controller secondo MicrosoftAsp.Net MVC 3 - Il Model View Controller secondo Microsoft
Asp.Net MVC 3 - Il Model View Controller secondo Microsoft
 
Android Test Driven Development
Android Test Driven DevelopmentAndroid Test Driven Development
Android Test Driven Development
 
Android Test Driven Development
Android Test Driven DevelopmentAndroid Test Driven Development
Android Test Driven Development
 
Dominare il codice legacy
Dominare il codice legacyDominare il codice legacy
Dominare il codice legacy
 
Unit testing 101
Unit testing 101Unit testing 101
Unit testing 101
 

More from Davide Cerbo

Kotlin: forse è la volta buona (Trento)
Kotlin: forse è la volta buona (Trento)Kotlin: forse è la volta buona (Trento)
Kotlin: forse è la volta buona (Trento)Davide Cerbo
 
Kotlin: maybe it's the right time
Kotlin: maybe it's the right timeKotlin: maybe it's the right time
Kotlin: maybe it's the right timeDavide Cerbo
 
From 0 to React with ES6 and Tests
From 0 to React with ES6 and TestsFrom 0 to React with ES6 and Tests
From 0 to React with ES6 and TestsDavide Cerbo
 
Kotlin: foss' a vota bona (could be the right time)
Kotlin: foss' a vota bona (could be the right time)Kotlin: foss' a vota bona (could be the right time)
Kotlin: foss' a vota bona (could be the right time)Davide Cerbo
 
Il web intelligente
Il web intelligenteIl web intelligente
Il web intelligenteDavide Cerbo
 
Working between the clouds (versione completa)
Working between the clouds (versione completa)Working between the clouds (versione completa)
Working between the clouds (versione completa)Davide Cerbo
 
Working between the clouds
Working between the cloudsWorking between the clouds
Working between the cloudsDavide Cerbo
 
Non Conventional Android Programming (English)
Non Conventional Android Programming (English)Non Conventional Android Programming (English)
Non Conventional Android Programming (English)Davide Cerbo
 
Non Conventional Android Programming (Italiano)
Non Conventional Android Programming (Italiano)Non Conventional Android Programming (Italiano)
Non Conventional Android Programming (Italiano)Davide Cerbo
 

More from Davide Cerbo (11)

Kotlin: forse è la volta buona (Trento)
Kotlin: forse è la volta buona (Trento)Kotlin: forse è la volta buona (Trento)
Kotlin: forse è la volta buona (Trento)
 
Kotlin: maybe it's the right time
Kotlin: maybe it's the right timeKotlin: maybe it's the right time
Kotlin: maybe it's the right time
 
From 0 to React with ES6 and Tests
From 0 to React with ES6 and TestsFrom 0 to React with ES6 and Tests
From 0 to React with ES6 and Tests
 
Kotlin: foss' a vota bona (could be the right time)
Kotlin: foss' a vota bona (could be the right time)Kotlin: foss' a vota bona (could be the right time)
Kotlin: foss' a vota bona (could be the right time)
 
Di cosa parlano?
Di cosa parlano?Di cosa parlano?
Di cosa parlano?
 
Adesso In Onda
Adesso In OndaAdesso In Onda
Adesso In Onda
 
Il web intelligente
Il web intelligenteIl web intelligente
Il web intelligente
 
Working between the clouds (versione completa)
Working between the clouds (versione completa)Working between the clouds (versione completa)
Working between the clouds (versione completa)
 
Working between the clouds
Working between the cloudsWorking between the clouds
Working between the clouds
 
Non Conventional Android Programming (English)
Non Conventional Android Programming (English)Non Conventional Android Programming (English)
Non Conventional Android Programming (English)
 
Non Conventional Android Programming (Italiano)
Non Conventional Android Programming (Italiano)Non Conventional Android Programming (Italiano)
Non Conventional Android Programming (Italiano)
 

Recently uploaded

Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...
Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...
Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...Associazione Digital Days
 
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...Associazione Digital Days
 
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...Associazione Digital Days
 
Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...
Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...
Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...Associazione Digital Days
 
ScrapeGraphAI: a new way to scrape context with AI
ScrapeGraphAI: a new way to scrape context with AIScrapeGraphAI: a new way to scrape context with AI
ScrapeGraphAI: a new way to scrape context with AIinfogdgmi
 
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”Associazione Digital Days
 

Recently uploaded (6)

Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...
Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...
Daniele Lunassi, CEO & Head of Design @Eye Studios – “Creare prodotti e servi...
 
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...
Alessio Mazzotti, Aaron Brancotti; Writer, Screenwriter, Director, UX, Autore...
 
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...
Federico Bottino, Lead Venture Builder – “Riflessioni sull’Innovazione: La Cu...
 
Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...
Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...
Luigi Di Carlo, CEO & Founder @Evometrika srl – “Ruolo della computer vision ...
 
ScrapeGraphAI: a new way to scrape context with AI
ScrapeGraphAI: a new way to scrape context with AIScrapeGraphAI: a new way to scrape context with AI
ScrapeGraphAI: a new way to scrape context with AI
 
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”
Edoardo Di Pietro – “Virtual Influencer vs Umano: Rubiamo il lavoro all’AI”
 

The Hitchhiker's Guide to testable code: semplici regole per scrivere codice semplice da testare

  • 1.
  • 2. The Hitchhiker's Guide to testable code semplici regole per scrivere codice semplice da testare Davide Cerbo - davidecerbo@gmail.com - JUG Roma Nicola Raglia - n.raglia@gmail.com - JUG Roma
  • 3. Non parleremo di... – XP Programming – Test-Driven Development – Agile – Scrum – etc etc
  • 4. ...ma parleremo di... come scrivere codice TESTABILE perchè l'unico modo per applicare le metodologie dette in precedenza è scrivere i TEST UNITARI e l'unico modo per scriverli è produrre codice TESTABILE
  • 5. Definizioni assortite Test: processo atto ad individuare carenze funzionali e non funzionali durante la fase di sviluppo del software. Test Unitario: è un test atto a verificare una componente elementare del software possibilmente in termini di isolamento dalle dipendenze Refactoring: è il processo che prevede una ristrutturazione del codice modificando il meno possibile le interfacce
  • 6. Ancora definizioni assortite Design Pattern: soluzione progettuale generale a un problema ricorrente Mock Object: oggetti destinati a simulare il comportamento di oggetti reali. Durante il test con i mock object abbiamo: o creazione mock o definizione del comportamento del mock object o esecuzione del test o verifica del comportamento del mock object
  • 8. Iniziamo dal costruttore public RubricaImpl(Properties properties, ApplicationContext applicationContext) { this.user = applicationContext.getAuthenticationContext().getUser(); this.url = properties.getProperty("url"); this.userName = properties.getProperty("userName"); this.password = properties.getProperty("password"); try { this.connection = DriverManager.getConnection(url, userName, password); } catch (SQLException e) { //gestione eccezione } this.database = new DatabaseImpl(connection); }
  • 9. Il nostro primo (non) Unit Test public void testConstructor() throws Exception { Properties properties = new Properties(); properties.load(new FileInputStream("database.properties")); ApplicationContext applicationContext = ApplicationContext.getContext(); Rubrica rubrica = new RubricaImpl(properties, applicationContext); } con i Mock Objects: public void testConstructor() throws Exception { Properties properties = new Properties(); properties.setProperty("user", "dbuser"); properties.setProperty("password","dbpassword"); properties.setProperty("url", "jdbc:db:///test"); ApplicationContext applicationContext = createMock(ApplicationContext.class); AuthenticationContext authContext = createMock(AuthenticationContext.class); expect(applicationContext.getAuthenticationContext()).andReturn(authContext); expect(authContext.getUser()).andReturn(createMock(User.class)); replay(authContext, applicationContext); Rubrica rubrica = new RubricaImpl(properties, applicationContext); verify(authContext, applicationContext); }
  • 10. Rispettiamo la legge public RubricaImpl(String url, String userName, String password, User user) { this.user = user; this.url = url; this.userName = userName; this.password = password; Connection connection=DriverManager.getConnection(url,userName,password); this.database = new DatabaseImpl(connection); } Per rispettare la legge di Demeter un oggetto può solo invocare i metodi: • propri • dei suoi parametri • di ogni oggetto che crea • delle sue variabili
  • 11. Mai dire CONTEXT public RubricaImpl(Properties properties, ApplicationContext applicationContext) { this.user = applicationContext.getUser(); this.url = properties.getProperty("url"); ....... ....... } public RubricaImpl(String url, String userName, String password, User user) { this.user = user; this.url = url; ....... ....... } applicationContext e properties sono oggetti di contesto quindi difficilmente testabili unitariamente e richiedono fatica aggiuntiva nel test con i mock object.
  • 12. Vietato affaticare public RubricaImpl(String url, String userName, String password, User user) { ..... this.userName = userName; Connection connection = DriverManager.getConnection(url,userName,password); this.database = new DatabaseImpl(connection); } public RubricaImpl(String url, String userName, String password, User user) { ..... this.userName = userName; this.database = DatabaseManager.getDatabase(url,userName,password); } Questa è una soluzione ma non va bene perché si usa un metodo statico
  • 13. Solo l'indispensabile public RubricaImpl(String url, String userName, String password, User user) { this.userName =userName; this.database = DatabaseManager.getDatabase(url,userName,password); } 2 SOLUZIONI DA EVITARE!!! public RubricaImpl(User user) { this.user = user; this.database = DatabaseSingleton.getInstance(); } Ecco fatta un po' di pulizia! Non era giusto far conoscere alla Rubrica le informazioni per accedere al database! Ma è spuntato un singleton e questo è male!
  • 14. Dependency Injection public RubricaImpl(Database database, User user) { this.user = user; this.database = database; } Il costruttore è stato alleggerito da responsabilità non proprie. Ma ora come lo usiamo?
  • 15. Pattern Abstract Factory public class RubricaFactoryImpl implements RubricaFactory { private final DatabaseFactory databaseFactory; public RubricaFactoryImpl(DatabaseFactory databaseFactory) { this.databaseFactory = databaseFactory; } public Rubrica getRubrica(User user) { return new RubricaImpl(databaseFactory.getDatabase(), user); } } La responsabilità di creare oggetti sarà sempre data ad una Factory o ad altri pattern creazionali.
  • 16. Passiamo al Database public class DatabaseFactoryImpl implements DataBaseFactory { private final Properties properties; public DatabaseFactoryImpl(Properties properties) { this.properties = properties; } public Database getDatabase(){ String url = properties.getProperty("url"); String userName = properties.getProperty("userName"); String password = properties.getProperty("password"); Connection connection = null; try { connection = DriverManager.getConnection(url, userName, password); } catch (SQLException e) { //gestione eccezione } return new DatabaseImpl(connection); } } DatabaseFactoryImpl non è testabile, andrebbe fatto ulteriore refactoring, ma il tempo è poco :(
  • 17. Il Test (quasi) finale (1/2)‫‏‬ public void testConstructor() throws Exception { Database database = createMock(Database.class); User user = createMock(User.class); replay(database, user); Rubrica rubrica = new RubricaImpl(database, user); verify(database, user); } Non c'è bisogno di descrivere comportamento per gli oggetti mock perchè il costruttore non fa niente altro che costruire l'oggetto. Ma le factory appena create?
  • 18. Il Test (quasi) finale (2/2)‫‏‬ public void testFactory() throws Exception { DatabaseFactory databaseFactory = createMock(DatabaseFactory.class); Database database = createMock(Database.class); User user = createMock(User.class); expect(databaseFactory.getDatabase()).andReturn(database); replay(databaseFactory, user, database); RubricaFactory rubricaFactory = new RubricaFactoryImpl(databaseFactory); Rubrica rubrica = rubricaFactory.getRubrica(user); verify(databaseFactory, user, database); assertNotNull(rubrica); }
  • 19. Gli obbiettivi raggiunti Single responsability Assegnare la giusta responsabilità Utilizzare la Dependency Injection Dividere il fare dal creare Evitare stati globali Design by Interface
  • 20. Andiamo avanti... public void publish(){ Context context = new InitialContext(); Object reference = context.lookup("PublisherService"); PublisherEjb home = (PublishEjb)PortableRemoteObject.narrow(reference,PublishEjb.class); PublisherService publisher = home.create(); publisher.publish(this); }
  • 21. Testiamolo... Totalmente non testabile in termini unitari!!!
  • 22. Via il Sevice Locator public RubricaImpl(Database database, User user, PublisherService publisher) { this.user = user; this.database = database; this.publisher = publisher; } public void publish(){ this.publisher.publish(this); } Iniettiamo una classe che abbia la responsabilità di pubblicare. Nel nostro caso lo farà tramite EJB, ma sarà semplice sostituire la tecnologia.
  • 23. Ancora non è finita... public RubricaImpl(Database database, User user) { this.user = user; this.database = database; } public void publishWith(PublisherService publisher){ publisher.publish(this); } Passare l'oggetto PublisherService al costruttore è errato perché non è necessario al normale ciclo di vita della Rubrica, ma serve solo nel caso di una richiesta di pubblicazione
  • 24. Problema solo spostato Abbiamo solo spostato il problema, infatti l'implementazione PublisherServiceEJB sarà intestabile unitariamente... ...ma fortunatamente la nuova specifica EJB 3.0 ci viene in aiuto eliminando il ServiceLocator Ma non è lo scopo di questo talk spiegare come :D
  • 25. Il Test finale public void testPublish() throws Exception { Database database = createMock(Database.class); User user = createMock(User.class); replay(database, user); Rubrica rubrica = new RubricaImpl(database, user); verify(database, user); PublisherService publisherService = createMock(PublisherService.class); publisherService.publish(rubrica); replay(publisherService, user); rubrica.publishWith(publisherService); verify(publisherService, user); }
  • 26. Bibliografia Google Testing Blog http://googletesting.blogspot.com/ Refactoring: Improving the Design of Existing Code (Martin Fowler)‫‏‬ http://www.refactoring.com/ Refactoring Workbook (William C. Wake)‫‏‬ http://xp123.com/rwb/ Applicare UML e Pattern (Craig Larman)‫‏‬ http://www.craiglarman.com Principi di ingegneria del software (Pressman)‫‏‬ http://highered.mcgraw-hill.com/sites/0072853182/ Wikipedia http://www.wikipedia.org
  • 27. Strumenti utili Unit Test: www.junit.org Test code coverage: http://cobertura.sourceforge.net/ http://emma.sourceforge.net/ Testability: http://code.google.com/p/testability-explorer/ http://testabilityexplorer.org/report Mock objects: http://www.easymock.org/ http://www.jmock.org/ http://code.google.com/p/mockito/
  • 28. I nostri contatti Davide Cerbo davidecerbo@gmail.com davide.cerbo@pro-netics.com http://jesty.it Nicola Raglia n.raglia@gmail.com nicola.raglia@pro-netics.com http://sourcengineering.org