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.

Sull’Interpretazione Dei “Giorni Uomo”

Nel contesto della consulenza software, la fatidica domanda “quanto tempo serve per terminare questo sviluppo” è più pericolosa di quello che possa sembrare, perché diversi stakeholder daranno diversi significati alla risposta. Così diversi che sarà anche sottointesa un’unità di misura diversa ad una risposta sia tipicamente espressa in giorni.

Ipotizziamo che il Professionista IT, che per conto della propria azienda di consulenza segue un progetto da un cliente affermi che “lo sviluppo delle nuove feature sarà terminato in 10 giorni”, intendendo un effort misurato in giorni uomo. Quindi immagina che lavorandoci saltuariamente con un collega, per portare avanti anche altre commesse, possa pubblicare in produzione le nuove feature tra una ventina di giorni, garantendo comunque che l’effort del reparto di sviluppo si attesti sui 10 giorni.

Il Collega del professionista, anche lui assegnato al progetto, capisce invece che si hanno ancora 10 giorni di tempo per lavorare al progetto, indipendentemente dal numero di persone che saranno coinvolte e questo lo rincuorerà poiché, vista la scadenza così prossima, e visto che quella routine proprio non sa come scriverla, sarà possibile delegare il gravoso compito a qualche altro collega.

Il Commerciale dell’azienda IT deve ovviamente assicurarsi che l’attività sia abbastanza redditizia per coprire non solo il costo di sviluppo ma anche una quota parte delle spese aziendali: l’affitto dell’ufficio, i costi di gestione, oltre che assicurare un’ovvia remunerazione. Il manager decide quindi di applicare un markup alla stima del professionista, proponendo al cliente una quotazione nella quale per trasparenza indica che l’effort sarà di 15 giorni uomo.

Il Manager lato cliente conosce il costo del progetto, e sapendo in quanto tempo sarà consegnato, con una rapida divisione calcola il costo giornaliero del professionista. Ottiene una cifra che tipicamente reputa troppo alta per il costo di uno sviluppatore e chiede quindi uno sconto all’azienda IT sulla quotazione del progetto perché comprende che la logica da sviluppare sia complessa, ma quel compenso giornaliero supera ogni limite di costo ragionevole.

Il PM del cliente, a cui il professionista riporta, pressato da più fronti, intende che le feature saranno rilasciate in produzione al massimo entro 15 giorni, che sarà considerato un lasso di tempo troppo lungo, quindi chiede di restringere i tempi di consegna a 10 giorni.

L’Esperto IT del cliente chiederà una riunione nella quale al professionista sarà chiesto di giustificare come mai lo sviluppo è stato quotato in 15 giorni, quando dalla sua esperienza, potrebbero tranquillamente bastarne 10. Il professionista, dal canto suo, sarà in linea di massima concorde con l’esperto del cliente ma dovrà arrovellarsi per ideare delle giustificazioni tecniche che giustifichino la stima comprensiva di mark up presentata dal proprio commerciale.

Tutto questo accade perché le varie figure coinvolte danno al concetto di tempo un significato totalmente diverso tra loro.

Per il Professionista la risposta è espressa in “giorni uomo previsti del team di sviluppo”. E’ una misurazione del tempo durante il quale gli sviluppatori dovranno impegnarsi per lo sviluppo del progetto. Il Professionista avrà anche incluso in questa misura un cuscinetto temporale per garantire di consegnare il lavoro entro il tempo espresso quindi più correttamente il Professionista intende la risposta come quel periodo di tempo che con una confidenza del 90% gli sviluppatori dovranno spendere nel progetto per garantire il rilascio delle funzionalità.

Per il Collega, i dieci giorni sono semplicemente dieci giorni di calendario. Se la consegna del progetto acquisisse una priorità altissima, egli sarebbe giustificato a pensare che l’Azienda dovrebbe includere nel progetto un numero illimitato di nuovi sviluppatori, a patto di rispettare la data di consegna, tra dieci giorni.

Per il Commerciale, la risposta da dare al Cliente, sentito il parere del Professionista, “è quell’ammontare di giornate che al costo per sviluppatore giornaliero preconcordato con il Cliente garantisce un’adeguata copertura dei costi e remunerazione”. E’ interessante notare come per il Commerciale, quindi, la “giornata” sia semplicemente un’unità di misura di valuta. Non euro, ma giornate, quindi.

Per il PM i quindici giorni sono, nello scenario migliore, il lasso di tempo entro il quale potrà smarcare l’attività come completata. Essendo lo scenario migliore, tuttavia, il tempo di completamento sarà in realtà aumentato del 50% nel progetto come misura precauzionale, allungando quindi l’attività a 21 giorni.

Il Manager del Cliente intende i 15 giorni come il costo economico della sola attività di sviluppo, dimenticandosi che ogni attività con scopo di lucro prevede ovviamente un margine economico. Eseguendo una semplice divisione ottiene un prezzo legato all’attività di un singolo sviluppatore che ritiene troppo elevato.

Infine l’Esperto IT del Cliente, intende i 15 giorni come puro effort, proprio come il Professionista, che però ne aveva stimati solo 10. L’Esperto ed il Professionista si confronteranno quindi su uno scostamento delle rispettive stime del 50% quando in realtà sono d’accordo almeno sul significato da dare alla quotazione su cui stanno discutendo.

Questo è quanto può accadere, nel peggiore dei casi, quando non c’è univocità nell’interpretazione dell’effort o precisione nella definizione dei termini o chiari rapporti tra fornitore e cliente.

 

 

 

First steps with Vagrant on Ubuntu with manually installed VirtualBox

Getting Vagrant – From Vagrant Site

If you are running an older version of Ubuntu, like I usually do (I run only LTS releases indeed) it’s probably better to download the latest Vagrant available on the Vagrant site.

$ cd /tmp
$ mkdir vagrant
$ cd vagrant
$ wget http://files.vagrantup.com/packages/a40522f5fabccb9ddabad03d836e120ff5d14093/vagrant_1.3.5_x86_64.deb
$ sudo dpkg --install vagrant_1.3.5_x86_64.deb

Install some machines

Vagrant is about building and running virtual machines described through a file, in order to let all developers to have the same machine. What vagrant really does is to keep locally a series of bare machines that will then be cloned to produce the environment you are describing. This means that before doing whatever in Vagrant, you should first of all populate this local repository with the bare machines you’d like to use.

On internet you can find some repositories of Vagrant boxes:
https://github.com/mitchellh/vagrant/wiki/Available-Vagrant-Boxes
http://www.vagrantbox.es/

So, let’s decide to test Vagrant with an ubuntu machine.

http://files.vagrantup.com/precise32.box

$ vagrant box add precise32 http://files.vagrantup.com/precise32.box
Downloading or copying the box...
Extracting box...te: 536k/s, Estimated time remaining: --:--:--)
Successfully added box 'precise32' with provider 'virtualbox'!

Let’s check whether the box has been successfully installed

$ vagrant box list
precise32 (virtualbox)

Setting up a Machine

Let’s try to set up a first experiment with Vagrant. More or less following what the official guides states. First of all let’s create a folder to host our experiment, and let’s move in it.

$ cd /tmp
$ mkdir vagrant-tutorial
$ cd vagrant-tutorial/

Time to initialize the Vagrant project. That is done with the “init” command. Just check what “init” does before issuing the command.

$ vagrant init -h

Then, let’s go with the initialisation.

$ vagrant init

A file called Vagrantfile appears after initialisation. Just give it a look. It’s full of commented options along with bried description, so, giving it a look can provide you with hints about what you can expect from Vagrant. The only not commented instruction is the one that tells Vagrant what machine we want to build on top of. It is currently the “base” box, that we are missing.

# Every Vagrant virtual environment requires a box to build off of.
config.vm.box = "base"

So, let’s change it to

# Every Vagrant virtual environment requires a box to build off of.
config.vm.box = "precise32"
and save the file.

Now, the magic all of you are waiting for

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
[default] Importing base box 'precise32'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Booting VM...
[default] Waiting for machine to boot. This may take a few minutes...
[default] Machine booted and ready!
[default] Mounting shared folders...
[default] -- /vagrant

Wow… do we really have a running machine ? Yes. Just type…

$ vagrant ssh

And you are connected to the vagrant running machine in a SSH session.

Turn off the machine

Exit from the SSH session and type…

$ vagrant status

To have a list of currently running machines. Turn off the machine with following command.

vagrant halt default
[default] Attempting graceful shutdown of VM...

And again…

$ vagrant status
Current machine states:

default poweroff (virtualbox)

Remove the machine

When you are done, just issue a destroy command to completely remove any reference to your machine.

$ vagrant destroy
Are you sure you want to destroy the 'default' VM? [y/N] y
[default] Forcing shutdown of VM...
[default] Destroying VM and associated drives...