1 / 29

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

The Hitchhiker's Guide to testable code. 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

Download Presentation

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

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. The Hitchhiker's Guide to testable code The Hitchhiker's Guide to testable code semplici regole per scrivere codice semplice da testare Davide Cerbo - davidecerbo@gmail.com - JUG RomaNicola Raglia - n.raglia@gmail.com - JUG Roma

  2. Non parleremo di... • XP Programming • Test-Driven Development • Agile • Scrum • etc etc

  3. ...ma parleremo di... come scrivere codiceTESTABILE perchè l'unico modo per applicare le metodologie dette in precedenza è scrivere iTEST UNITARI e l'unico modo per scriverli è produrre codiceTESTABILE

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

  5. 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: • creazione mock • definizione del comportamento del mock object • esecuzione del test • verifica del comportamento del mock object

  6. Esempio di codice brutto

  7. 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);    }               Iniziamo dal costruttore

  8.               Il nostro primo (non) Unit Test publicvoid 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);    }

  9. 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);  }               Rispettiamo la legge 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

  10. 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;         .......                 ....... }               Mai dire CONTEXT applicationContext e properties sono oggetti di contesto quindi difficilmente testabili unitariamente e richiedono fatica aggiuntiva nel test con i mock object.

  11.  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);    }               Vietato affaticare Questa è una soluzione ma non va bene perché si usa un metodo statico

  12. 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();    }               Solo l'indispensabile 2 SOLUZIONI DA EVITARE!!! 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!

  13. public RubricaImpl(Database database, User user) {this.user = user;this.database = database;    }               Dependency Injection Il costruttore è stato alleggerito da responsabilità non proprie. Ma ora come lo usiamo?

  14. 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);    } }               Pattern Abstract Factory La responsabilità di creare oggetti sarà sempre data ad una Factory o ad altri pattern creazionali.

  15.              Passiamo al Database publicclass 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 :(

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

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

  18.               Gli obbiettivi raggiunti • Single responsability • Assegnare la giusta responsabilità • Utilizzare la Dependency Injection • Dividere il fare dal creare • Evitare stati globali • Design by Interface

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

  20.               Testiamolo... Totalmente non testabile in termini unitari!!!

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

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

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

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

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

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

  27. Nicola Raglia n.raglia@gmail.com nicola.raglia@pro-netics.com http://sourcengineering.org                I nostri contatti Davide Cerbo davidecerbo@gmail.com davide.cerbo@pro-netics.com http://jesty.it

  28.                Q&A Q&A Q&A Q&A Q&A

More Related