The Hitchhiker's
            Guide to
          testable code



semplici regole per scrivere codice semplice da testare
 ...
Non parleremo di...

   – XP Programming

– Test-Driven Development

         – Agile

        – Scrum

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

                 come scrivere codice
                   TESTABILE
perchè l'unico modo per applicar...
Definizioni assortite

Test: processo atto ad individuare carenze funzionali e non
funzionali durante la fase di sviluppo ...
Ancora definizioni assortite

Design Pattern: soluzione progettuale generale a un
problema ricorrente

Mock Object: oggett...
Esempio di codice brutto
Iniziamo dal costruttore

public RubricaImpl(Properties properties, ApplicationContext applicationContext) {
    this.user...
Il nostro primo (non) Unit Test
public void testConstructor() throws Exception {
    Properties properties = new Propertie...
Rispettiamo la legge

public RubricaImpl(String url, String userName, String password, User user) {
   this.user = user;
 ...
Mai dire CONTEXT
public RubricaImpl(Properties properties, ApplicationContext applicationContext) {
    this.user = applic...
Vietato affaticare
public RubricaImpl(String url, String userName, String password, User user) {
    .....
   this.userNam...
Solo l'indispensabile
public RubricaImpl(String url, String userName, String password, User user) {
    this.userName =use...
Dependency Injection

public RubricaImpl(Database database, User user) {
    this.user = user;
    this.database = databas...
Pattern Abstract Factory

public class RubricaFactoryImpl implements RubricaFactory {
  private final DatabaseFactory data...
Passiamo al Database
public class DatabaseFactoryImpl implements DataBaseFactory {
  private final Properties properties;
...
Il Test (quasi) finale (1/2)‫‏‬

public void testConstructor() throws Exception {
    Database database = createMock(Datab...
Il Test (quasi) finale (2/2)‫‏‬

public void testFactory() throws Exception {
    DatabaseFactory databaseFactory = create...
Gli obbiettivi raggiunti

Single responsability
Assegnare la giusta responsabilità
Utilizzare la Dependency Injection
Divi...
Andiamo avanti...

public void publish(){
  Context context = new InitialContext();
  Object reference = context.lookup("P...
Testiamolo...

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

public RubricaImpl(Database database, User user, PublisherService publisher) {
    this.user = user...
Ancora non è finita...

public RubricaImpl(Database database, User user) {
    this.user = user;
    this.database = datab...
Problema solo spostato

Abbiamo solo spostato il problema, infatti
l'implementazione PublisherServiceEJB sarà
intestabile ...
Il Test finale

public void testPublish() throws Exception {
    Database database = createMock(Database.class);
    User ...
Bibliografia
Google Testing Blog
http://googletesting.blogspot.com/

Refactoring: Improving the Design of Existing Code (M...
Strumenti utili
Unit Test:
www.junit.org

Test code coverage:
http://cobertura.sourceforge.net/
http://emma.sourceforge.ne...
I nostri contatti

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

Nicola Raglia
n.raglia@...
Q&A




Q&A
Q&A
The Hitchhiker's Guide to testable code: semplici regole per scrivere codice semplice da testare
Upcoming SlideShare
Loading in …5
×

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

1,447 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 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,447
On SlideShare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
11
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

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

  1. 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. 2. Non parleremo di... – XP Programming – Test-Driven Development – Agile – Scrum – etc etc
  3. 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. 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. 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. 6. Esempio di codice brutto
  7. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 20. Testiamolo... Totalmente non testabile in termini unitari!!!
  21. 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. 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. 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. 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. 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. 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. 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. 28. Q&A Q&A Q&A

×