-+  Documenti
 |-  Bibliografia
 |-  Articoli
 |-  Perlfunc
 |-  F.A.Q.
 |-  F.A.Q. iclp
-+  Eventi
-+  Contatti
-+  Blog
-+  Link



 

Versione stampabile.


Flavio Poletti
Consulente in telecomunicazioni per una piccola società a Roma, usa Perl per lavoro e per diletto, con particolare attenzione all'automazione di processi ripetitivi e noiosi.
www.polettix.it

Test::MockObject è un modulo che permette di costruire oggetti "finti", che fanno finta di essere qualcos'altro. A che servono? Presto detto - ma prima un po' di suspence.

Partiamo da quello che avete...

Supponiamo che abbiate un bel progetto ad oggetti, in cui ogni funzionalità è stata incapsulata dentro la sua classe, ed avete ridotto al minimo tutte le dipendenze fra classi per fare in modo da avere un sistema pulito. Con tutti gli sforzi che potete compiere, non potete sicuramente prescindere da un fatto: gli oggetti si devono parlare fra loro, altrimenti servono a poco.

Per fissare le idee, nel vostro progetto c'è una classe Façade che rappresenta un sistema di comunicazioni a messaggi, con un metodo per spedire i messaggi:

package CommunicatorFacade;

# ...

sub send_message {
   my $self = shift;
   my $message_reference = shift;

   # Fai quello che devi fare...
}

1; # Fine di CommunicatorFacade

Avete voluto disaccoppiare gli oggetti il più possibile, utilizzando una classe Factory per accedere ai vari oggetti di interfaccia:

package Factory;

# ...

sub get_communicator {
   my $self = shift;

   # Restituisce un riferimento all'oggetto facade del sottosistema di
   # comunicazione, qualunque esso sia, creandolo se necessario. In
   # poche parole, quello che ci si aspetta da una factory :)
}

1;  # Fine di Factory

Dovete ora sviluppare una nuova classe che, al verificarsi di determinate condizioni di errore, invii un messaggio ad un destinatario differente a seconda della gravità del messaggio stesso. Alla creazione l'oggetto riceve un reference alla classe Factory da utilizzare per accedere al resto dei sottosistemi:

package Monitor;

sub new {
   my $class = shift;
   my $factory = shift;
   # Memorizza il riferimento alla $factory da utilizzare da qui in poi
   return bless { _factory => $factory }, $class;
}

# ...

# Metodo chiamato al verificarsi di una condizione di errore
sub alarm {
   my $self = shift;
   my ($level, $message) = @_;

   # Stabilisci il destinatario a seconda della gravità del messaggio.
   my %recipients = (
      info  => 'info@example.com',
      panic => 'ceo@example.com'
   );
   # Per default, il destinatario è support@example.com
   my $recipient = $recipients{$level} || 'support@example.com';

   # Invia il messaggio: prende il "comunicatore" da utilizzare dalla
   # factory, e lo usa! Assumiamo che i messaggi siano rappresentati da
   # riferimenti ad hash contenenti i vari campi del messaggio stesso.
   my $communicator = $self->{_factory}->get_communicator();
   $communicator->send_message({recipient => $recipient, message => $message});
}

1; # Fine di Monitor

Niente di difficile concettualmente, ma poiché siete coscienziosi volete avere dei test per la classe - oops, dovete metter su il sistema di comunicazione! Questo ha varie conseguenze:

  • il set-up del sistema di comunicazione potrebbe non essere banale o non sempre disponibile;
  • modifiche in corso sul sistema di comunicazione verrebbero ad influire anche sulla vostra nuova classe;
  • dovete verificare che sia mandato il messaggio corretto al destinatario corretto, il che richiede maggiore intrusività del test all'interno del sistema di comunicazione.

... Test::MockObject all'arrembaggio!

Ora che ci siamo un po' scaldati, possiamo cominciare a costruire l'ambiente di test per Monitor. Come abbiamo visto, dobbiamo generare due oggetti finti: uno per la Factory, l'altro per assumere il ruolo di FacadeCommunicator. Cominciamo da questo:

my $sent_message;  # Conterrà il messaggio inviato
my $fake_communicator = Test::MockObject->new();
$fake_communicator->mock('send_message', sub { $sent_message = $_[1] });

In pratica, il nostro oggetto finto espone un metodo send_message, come CommunicatorFacade, solo che invece di fare cose complicate salva il messaggio all'interno della variabile $sent_message, che potrà essere controllata successivamente.

L'oggetto Factory finto è ancora più semplice:

my $fake_factory = Test::MockObject->new();
$fake_factory->set_always('get_communicator', $fake_communicator);

Come si può vedere, l'unica cosa che fa il suo metodo get_communicator() è restituire $fake_communicator, in modo che Monitor utilizzi quello.

Siamo ora in grado di creare un oggetto Monitor in modo piuttosto semplice:

my $monitor = Monitor->new($fake_factory);

La chiamata ad alarm() farà sì che venga modificata la variabile globale $sent_message, come visto.

Quello che segue mette insieme tutti i pezzi visti, integrandoli all'interno di un file di test completo:

use Test::More tests => 2;
use Test::MockObject;

BEGIN { use_ok('Monitor'); }

# Qui effettuiamo il set-up del nostro ambiente di test.
my $sent_message;  # Conterrà il messaggio inviato
my $fake_communicator = Test::MockObject->new();
$fake_communicator->mock('send_message', sub { $sent_message = $_[1] });

my $fake_factory = Test::MockObject->new();
$fake_factory->set_always('get_communicator', $fake_communicator);


# A questo punto possiamo creare un oggetto di tipo Monitor...
my $monitor = Monitor->new($fake_factory);

# ... e cominciare con i test veri e propri
$sent_message = undef;
$monitor->alarm('info', 'entrato utente');
is_deeply($sent_message,
          {recipient => 'info@example.com', message => 'entrato utente'},
          "messaggio inviato correttamente");

Grazie a $sent_message il ciclo si chiude: possiamo infatti verificare che il messaggio inviato (completo di destinatario) sia corretto mediante un confronto fra quanto spedito e quanto ci si aspettava. Lanciando lo script abbiamo dunque:

1..2
ok 1 - use Monitor;
ok 2 - messaggio inviato correttamente

Glossario

In questo articolo abbiamo introdotto un paio di termini (Factory, Façade) comuni in una comunicazione a Pattern. Un Pattern è sostanzialmente la soluzione corretta ad un dato problema specifico, soluzione che si è venuta formando nella comunità dei progettisti e dei programmatori in anni di tentativi. L'utilizzo dei pattern nel linguaggio consente di esprimere idee precise in modo conciso, facendo inoltre riferimento a soluzioni che sono divenute standard.

Una Factory è una classe (o oggetto della classe) che serve per generare altri oggetti (factory in inglese sta per fabbrica). Sostanzialmente, per lasciare al programmatore la flessibilità di decidere a posteriori quali oggetti particolari utilizzare a run-time, si delega l'operazione di creazione degli oggetti stessi alla factory, che dunque è il punto in cui viene centralizzata la produzione di oggetti (o di gran parte di essi).

Una Façade (facciata) è invece una classe che nasconde (proprio come una facciata) un sistema complesso. Prendiamo il sistema di comunicazione: potrebbe avere molti oggetti che lo compongono, tipo un database degli utenti, dei sistemi dedicati per i differenti tipi di invio (e-mail ed SMS, ad esempio), e così via. Invece di richiedere all'utente di sapere tutte queste cose, ed obbligarlo ad interagire con le varie parti, confezioniamo un oggetto facciata che ci consente di accedere al sistema di comunicazione in modo semplice e senza troppi problemi. Ciò va ovviamente a discapito della flessibilità, ma nel 99% delle volte questo va bene all'utente finale.

Titoli di coda

Un'occhiata alla documentazione di Test::MockObject è a questo punto d'obbligo se volete saperne di più. L'autore, chromatic (Perl.com, Perl Monks), ha anche scritto qualche articolo sul testing su Perl.com (come ad esempio An Introduction to Testing e A Test::MockObject Illustrated Example) ed un libro (Perl Testing: A Developer's Notebook di O'Reilly, scritto insieme a Ian Langworth).

Se volete saperne di più sui pattern, probabilmente la cosa migliore è procurarvi un libro sulla progettazione del software, tipo Applying UML and Patterns--An Intro to OOA/D and Iterative Development di Craig Larman. Il testo di riferimento è Design Patterns: Elements of Reusable Object-Oriented Software, della cosiddetta gang of four, ma considerate che osfameron lo usava per curare l'insonnia. Se volete rivolgervi al web, il punto di riferimento è probabilmente la Portland Pattern Repository's Wiki, anche se ci vuole un po' di tempo per cominciare a capirci qualcosa.

Buona lettura!


Ti è piaciuto questo articolo? Iscriviti al feed!











Devo ricordare i dati personali?






D:
Annunci Google