La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

Davide Cerbo - - JUG Roma Nicola Raglia - - JUG Roma The Hitchhiker's Guide to testable code semplici regole.

Presentazioni simili


Presentazione sul tema: "Davide Cerbo - - JUG Roma Nicola Raglia - - JUG Roma The Hitchhiker's Guide to testable code semplici regole."— Transcript della presentazione:

1

2 Davide Cerbo - - JUG Roma Nicola Raglia - - JUG Roma The Hitchhiker's Guide to testable code semplici regole per scrivere codice semplice da testare The Hitchhiker's Guide to testable code

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

7 Esempio di codice brutto

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

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 Refactoring: Improving the Design of Existing Code (Martin Fowler)‏ Refactoring Workbook (William C. Wake)‏ Applicare UML e Pattern (Craig Larman)‏ Principi di ingegneria del software (Pressman)‏ Wikipedia

27 Strumenti utili Unit Test: Test code coverage: Testability: Mock objects:

28 I nostri contatti Davide Cerbo Nicola Raglia

29 Q&A


Scaricare ppt "Davide Cerbo - - JUG Roma Nicola Raglia - - JUG Roma The Hitchhiker's Guide to testable code semplici regole."

Presentazioni simili


Annunci Google