Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

29

description

The Hitchhiker's Guide to testable code. The Hitchhiker's Guide to testable code. semplici regole per scrivere codice semplice da testare. Davide Cerbo - [email protected] - JUG Roma Nicola Raglia - [email protected] - JUG Roma. Non parleremo di. XP Programming - PowerPoint PPT Presentation

Transcript of Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

Page 1: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma
Page 2: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

Davide Cerbo - [email protected] - JUG RomaNicola Raglia - [email protected] - JUG Roma

The Hitchhiker's Guide to

testable code

semplici regole per scrivere codice semplice da testare

The Hitchhiker's Guide to

testable code

Page 3: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              Non parleremo di... 

– XP Programming

– Test-Driven Development

– Agile

– Scrum

– etc etc  

Page 4: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              ...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

Page 5: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

                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

Page 6: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

             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 mocko definizione del comportamento del mock objecto esecuzione del testo verifica del comportamento del mock object

Page 7: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              Esempio di codice brutto

 

Page 8: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              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);    }

Page 9: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              Il nostro primo (non) Unit Testpublic 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);    }

Page 10: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              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

Page 11: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              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.

Page 12: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              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

Page 13: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              Solo l'indispensabile

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

 

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!

2 SOLUZIONI DA EVITARE!!!

Page 14: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              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?

Page 15: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              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.

Page 16: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

             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 :(

Page 17: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

               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?

Page 18: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              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);    }

Page 19: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              Gli obbiettivi raggiunti

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

Page 20: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

               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); }

Page 21: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

              Testiamolo...

Totalmente non testabile in termini unitari!!!

Page 22: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

               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.

Page 23: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

               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

Page 24: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

             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

Page 25: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

               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);    }

Page 26: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

               Bibliografia

Google Testing Bloghttp://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/

Wikipediahttp://www.wikipedia.org

Page 27: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

               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/

Page 28: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

               I nostri contatti

Davide [email protected]@pro-netics.comhttp://jesty.it

Nicola [email protected]@pro-netics.comhttp://sourcengineering.org

Page 29: Davide Cerbo - davidecerbo@gmail - JUG Roma Nicola Raglia - n.raglia@gmail - JUG Roma

               Q&A

Q&AQ&AQ&AQ&A