Edizione Corso Test Driven Development Presso Azienda Settore Finance

Lo Studio Ingegneria Demichelis ha completato l’erogazione di un’altra edizione del corso TDD presso un’importante azienda operante nel settore finance. I numerosi partecipanti hanno dato vita ad un’edizione del corso molto attiva dove con svariati esempi sono stati trasmessi i fondamenti di Test Driven Development con Java.

Corso Test Driven Development Settembre / Ottobre 2015
Corso Test Driven Development Settembre / Ottobre 2015

Indirect Input & Output in Unit Testing

Un test unitario definisce un particolare scenario di funzionamento di un unica “unità” che può essere definita come: system under test (SUT), object under test o unit under test, proprio per sottolineare la focalizzazione di cui è oggetto nel test.

 

Per eseguire il SUT nello scenario desiderato è necessario fornirgli un input, cioè l’insieme di informazioni che stimolano la funzionalità in via di definizione attraverso il test unitario. l’output comprende tutte le informazioni restituite dal SUT a seguito alla stimolazione, e su queste sono espressi i vincoli e le attese del test unitario.

 

Raramente, però, un oggetto può portare a termine il proprio compito in perfetto isolamento; il più delle volte esso necessita di collaboratori. L’output dei collaboratori rappresenta per il SUT una forma di input, meglio descritto come input indiretto. Si noti che a parità di input, l’output del SUT dipenderà dall’input indiretto proveniente dai collaboratori.

 

Un test unitario dovrebbe anche definire quali siano le ripercussioni il SUT deve avere sui collaboratori nello scenario eseguito. Tali ripercussioni sono l’indirect output del SUT e possono anch’esse essere oggetto di verifica da parte del test unitario.

indirect_input_output

Si immagini ad una comune applicazione web che permetta ai nuovi utenti di registrarsi fornendo email e password. Se la registrazione va a buon fine, il nuovo utente riceve all’indirizzo di posta dichiarato una email di conferma.

Il processo di registrazione è gestito da un oggetto di tipo RegistrationController che utilizza due collaboratori:

  • un’istanza di UserRepository, servizio che incapsula l’accesso al database degli utenti permettendo di crearne di nuovi e di verificare se un’email sia già utilizzata;
  • un’istanza di Notifier, servizio che permette di inviare email di conferma.

Si ipotizzi di impostare il test unitario che specifichi il comportamento di RegistrationController in caso di registrazione di un nuovo utente con username ancora non utilizzato. Sommariamente lo scenario che si vuole sia verificato dal SUT è il seguente:

  1. Dati…
    1. uno UserRepository (collaborator) che ancora non ha censito alcun utente (e che quindi fornirà un indirect input rappresentante l’assenza di alcun utente),
    2. un Notifier (collaborator),
    3. un RegistrationController (SUT) che usa i precedenti oggetti come collaborator,
  2. quando…
    1. si invoca il metodo newRegistration() di RegistrationController fornendo un’email e una password (stimolazione del SUT)
  3. deve accadere che…
    1. newRegistration() restituisca la stringa “registration_ok” (verifica dell’output)
    2. RegistrationController invochi il metodo sendConfirmationEmail() di Notifier, passando come email la stessa usata per registrarsi (verifica dell’indirect output)

 

Ovviamente questo è solo uno dei vari Test Unitari che andrebbero definiti. Lo scopo in questo caso è tuttavia quello di focalizzare l’attenzione su due punti.

  1. l’output del SUT, verificato al passo 3a) dipende anche dall’indirect input fornito dal collaboratore definito al punto 1a).
  2. Non solo un test unitario deve verificare l’output del SUT, ma anche le ripercussioni che il SUT ha sugli altri collaboratori.

 

Sia impostare l’indirect input che verificare l’indirect output sono due attività che possono essere realizzate seguendo diversi stili ed utilizzando diverse tecnologie, ma questo è materiale per un altro post.
Questi e altri concetti, esempi ed esercizi sono presentati in dettaglio nel corso Test Driven Development organizzato dallo Studio Ingegneria Demichelis presso la tua Azienda, sul sito dedicato trovi tutte le informazioni, ma per qualsiasi approfondimento contattaci pure.

Codice scoperto a seguito di refactoring

Le attività di refactoring e di manutenzione possono inavvertitamente portare alla rimozione della copertura di una classe, ma conoscendo il problema e utilizzando opportunamente gli strumenti a disposizione è possibile arginare il problema.

Scenario

Si ipotizzi che durante una sessione di sviluppo con TDD si definisca una suite di test TestA che specifica il comportamento della classe A. TestA stimolerà quindi un oggetto di tipo A e la classe A sarà quindi coperta dai test. Per semplificare l’esempio immaginiamo pure che la copertura di A sia pari al 100%.

A class covered by ATest
A class covered by ATest

Aderendo al tradizionale workflow red / green / refactor, ci si accorge che come sia possibile rifattorizzare il codice estraendo una nuova classe X, mantenendo in A un riferimento ad X. A questo punto quindi X sarà coperta solo grazie al fatto che TestA invoca metodi di A che a loro volta delegano alcuni compiti a X, in funzione di collaboratore.
Proseguendo lo sviluppo, si immagini di aggiungere una nuova suite TestB che definisce il comportamento della nuova classe B, che a sua volta utilizza anch’essa un oggetto di tipo X. Se TestB è scritto in stile “mockista”, l’oggetto di tipo B delegherà presumibilmente ad un test double di X e non ad un oggetto concreto di tipo X. Le classi A, B e X, comunque, risultano ancora coperte.

X indirectly tested.
X indirectly tested

 

Problema

Durante l’attività di manutezione ci si rende conto che la classe A non serve più e quindi questa viene rimossa, insieme a TestA. Quello che accade è che a questo punto X non è più coperta da alcun test, creando quindi una possibile fonte di errori di regressione.

X scoperta a causa della rimozione del test indiretto
X scoperta a causa della rimozione del test indiretto

 

Risoluzione

Come è possibile risolvere o prevenire questo problema ?

Evitare la copertura indiretta

Una prima idea è che quando durante un refactoring si estrae una nuova classe si dovrebbe rifattorizzare anche il corrispondente codice di test in modo da estrarne la sezione che stimola la nuova classe.

Garantire sempre una copertura diretta
Garantire sempre una copertura diretta

Coverage per l’identificazione di classi scoperte

Un’altra possibilità è quella di utilizzare sistemi di misurazione della copertura, con i quali ci si potrebbe immediatamente accorgere dopo la rimozione di A che la copertura di X è allo 0% e quindi correre immediatamente ai ripari.

Riferimenti

http://martinfowler.com/articles/mocksArentStubs.html

Corso TDD presso azienda IT

Da pochi giorni è terminato il corso su Test Driven Development che ho erogato presso un’importante azienda di sviluppo e gestione di sistemi software operante nel settore dei trasporti.

I corsisti sono stati esposti alle idee fondamentali di Test Driven Development.

  • Ciclo di sviluppo test / barra rossa / sviluppo / barra verde / refactoring.
  • Isolamento delle unità attraverso l’uso di mock

Sono stati presentati ai corsisti una serie di casi studio tratti dalla letteratura che mostrano come Test Driven Development abbia una positiva ricaduta sul livello di qualità del software prodotto.

Sono stati presentati anche gli impatti che si possono attendere dall’applicazione di Test Driven Development, sia a livello tecnologico che a livello aziendale.

Dal lato pratico numerose sono state le esercitazioni che hanno portato i corsisti a comprendere le funzionalità di alcune librerie e strumenti standard utilizzati tipicamente nel  Test Driven Development applicato a progetti Java: JUnit, Mockito, Eclispe, ECLEmma.

Il questionario di valutazione ha permesso di dimostrare come il corso, con una miscela di elementi teorici, esercitazioni mirate, descrizioni del contesto e commenti su esperienze vissute sul campo, abbia riscosso il gradimento di tutto il gruppo di corsisti, composto da figure professionali che coprono tutto l’arco di competenze necessarie allo sviluppo della parte tecnica di un sistema software.

Corso TDD di luglio 2013
Edizione di luglio 2013 del corso Test Driven Development

Seminario TDD presso primaria azienda nel settore Telco

Alcune immagini del seminario su Test Driven Development che ho tenuto ieri presso la sede di un’importante azienda operante nel settore telco. Il pubblico, composto per lo più di programmatori Java, C e C++ è stato esposto ai principi fondamentali di TDD.

TDD class held on May 2012
TDD class held on May 2012

I partecipanti hanno appreso tramite esempi interattivi realizzati durante il corso come utilizzare TDD con un tipico stack tecnologico Java composto da IDE Eclipse, JUnit, Mockito ed EclEmma.

Il corso è stato valutato positivamente dai partecipanti che hanno espresso il loro giudizio compilando una scheda di valutazione anonima su sei aspetti del corso.