Your SlideShare is downloading. ×
The Hitchhiker's Guide to testable code: semplici regole per scrivere codice semplice da testare
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

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

1,228
views

Published on

Nicola e Davide vi guideranno in uno spericolato refactoring di un codice poco gradevole alla vista di qualsiasi buon programmatore con lo scopo di illustrare i principali problemi che normalmente …

Nicola e Davide vi guideranno in uno spericolato refactoring di un codice poco gradevole alla vista di qualsiasi buon programmatore con lo scopo di illustrare i principali problemi che normalmente affligono il nostro povero codice rendendo difficile la scrittura di fantastici e utili test unitari. Verranno spiegati i principi da rispettare per ottenere un codice facile da testare quali: dependency injection, law of demeter, uso del pattern factory e builder, corretta scrittura dei costruttori, come scovare nomi pericolosi. Ed illustrate le pratiche da evitare: pattern singleton, stati globali, service locator, scrittura di classi con troppa responsabilità. Alla fine del talk verranno presentati alcuni link, software e libri utili nella scrittura di test unitari.

Published in: Technology

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,228
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
11
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. 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
  • 2. Non parleremo di... – XP Programming – Test-Driven Development – Agile – Scrum – etc etc
  • 3. ...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
  • 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: o creazione mock o definizione del comportamento del mock object o esecuzione del test o verifica del comportamento del mock object
  • 6. Esempio di codice brutto
  • 7. 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); }
  • 8. 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); }
  • 9. 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
  • 10. 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.
  • 11. 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
  • 12. 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!
  • 13. 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?
  • 14. 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.
  • 15. 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 :(
  • 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. 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
  • 28. Q&A Q&A Q&A

×