Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

FlexUnit: Unit Testing per applicazioni Flash e ActionScript

Tenere il codice sempre sotto controllo con i test automatici
Tenere il codice sempre sotto controllo con i test automatici
Link copiato negli appunti

Quando sviluppiamo un'applicazione, specie di medie o grandi dimensioni, vogliamo ridurre al minimo la possibilità di avere bug nel codice. Inoltre vogliamo evitare che una modifica, intacchi le funzionalità già testate e di cui è stato verificato il corretto funzionamento.

Per ottenere questi risultati è importante prevedere una fase di test durante il processo di sviluppo del software.

Le metodologie tradizionali prevedono la fase di test alla fine del ciclo di sviluppo, quando il software è già realizzato. Le metodologie agili, in particolare eXtreme Programming, prevedono che la fase di test sia realizzata prima dello sviluppo del codice: questa tecnica è chiamata Test Driven Development e, per quanto possa sembrare bizzarra, permette di ridurre significativamente il numero di bug presenti nel nostro codice.

Al di là della metodologia utilizzata, è buona pratica testare le nostre applicazioni ed è ancora meglio se i test possono essere lanciati automaticamente. Esistono molti framework per la creazione e l'esecuzione dei test: il padre di tutti gli ambienti di test è Junit realizzato per Java. Per Flex, invece, abbiamo a disposizione FlexUnit, sviluppato da Adobe come progetto open source.

Miniglossario dello Unit Testing

Prima di iniziare a sviluppare i test per una nostra applicazione, è bene chiarire il significato di alcuni termini.

Termine Descrizione
Test case è un insieme di asserzioni che verificano il corretto funzionamento di un'operazione. Consiste nel controllare il corretto output di una funzione o di un metodo, per input noti
Test Suite è un insieme di test case e viene utilizzata per testare un programma o un insieme di funzionalità. Può contenere, se necessario, opzioni di configurazione del programma da testare, in modo che i test possano essere eseguiti simulando in maniera completa il funzionamento dell'applicazione
Code coverage è una pratica di testing e indica il grado con cui il codice sorgente della nostra applicazione è coperto dai test

Creare un'applicazione per eseguire i test

FlexUnit è un framework per unit testing per ActionScript 3.0 e Flex e permette di eseguire i test attraverso un test runner grafico.

Per utilizzare FlexUnit, dobbiamo creare un'applicazione dedicata all'esecuzione dei test: l'applicazione deve sfruttare un component di tipo TestRunnerBase per l'esecuzione dei test unitari.

Abbiamo bisogno della libreria FlexUnit.swc, reperibile alla sezione download del sito del progetto. Una volta scaricata la libreria, dobbiamo includerla nel nostro progetto aggiungendola al "library path".

Figura 1. Includere la libreria nel progetto
Includere la libreria nel progetto

Su Flash Builder 4 l'inserimento della libreria è automatizzato grazie al bottone "FlexUnit Test" che troviamo all'interno del menu di debug, nella barra degli strumenti.

Figura 2. Bottone FlexUnit Test
Bottone FlexUnit Test

In questo caso utilizziamo però la versione 4.0 del framework per i test, ancora in versione RC nel momento in cui scriviamo, mentre in questo articolo utilizzeremo la 0.9, che invece è stabile. I concetti sono comunque sovrapponibili.

A questo punto, possiamo costruire l'applicazione per l'esecuzione dei test. Tra i "custom components" dobbiamo individuare il component TestRunnerBase e trascinarlo in modo visuale sullo stage, che conterrà dell'applicazione per i test. Bisogna dare un nome all'applicazione di test e lo facciamo scrivendolo nella proprietà id del componente (nel nostro esempio "test_app").

Lanciamo l'applicazione. Apparirà un'interfaccia simile a quella mostrata nella seguente figura:

Figura 3. Interfaccia di FlexUnit Runner
Interfaccia di FlexUnit Runner

Il component TestRunnerBase deve essere inizializzato per eseguire un insieme di test. Di seguito è riportato il codice che inizializza l'applicazione per i test.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:flex="flexunit.flexui.*"
                creationComplete="onCreationComplete()">

<mx:Script>
<![CDATA[

import flexunit.framework.TestSuite;

private function onCreationComplete():void {
  test_app.test = createSuite();
  test_app.startTest();
}

private function createSuite():TestSuite {
  var suite:TestSuite = new TestSuite();
  return suite;
}

]]>
</mx:Script>

<flex:TestRunnerBase x="0" y="0" id="test_app">
</flex:TestRunnerBase>

</mx:Application>

Allo scatenarsi dell'evento creationComplete dell'applicazione viene richiamato il metodo onCreationComplete che crea una TestSuite vuota e la assegna al component TestRunnerBase che si occupa della sua esecuzione.

Creare un TestCase

Supponiamo di aver creato, nel nostro progetto, una classe semplicissima MathOperation che implementa le quattro operazioni fondamentali della matematica (somma, differenza, moltiplicazione, divisione) e che vogliamo testarne il funzionamento.

Di seguito è riportato il codice della classe MathOperation

public class MathOperation
{	
  public function add(oper1:Number, oper2:Number): Number {
    return oper1+oper2;
  }
  
  public function sub(oper1:Number, oper2:Number): Number {
    return oper1-oper2;
  }
  
  public function mul(oper1:Number, oper2:Number): Number {
    return oper1*oper2;
  }
  
  public function div(oper1:Number, oper2:Number): Number {
    return oper1/oper2;
  }
}

Per scrivere lo unit test della classe MathOperation, dobbiamo creare una classe che estende TestCase e che chiamiamo MathOperationTest. FlexUnit usa le proprietà di introspezione di AS3 (reflection) per eseguire i metodi per il test: tutti i metodi della classe MathOperationTest che iniziano con la parola test verrano eseguiti all'interno della TestSuite.

Per il momento, decidiamo di scrivere il test solo per il metodo add(): la classe MathOperationTest avrà, quindi, la seguente implementazione:

package
{
  import flexunit.framework.TestCase;
  
  public class MathOperationTest extends TestCase
  {
    public function testAdd():void {
      var math:MathOperation = new MathOperation();
      
      assertEquals(4, math.add(1, 3));
      assertEquals(10, math.add(7, 3));
    }
  }
}

Il test vero e proprio del metodo add() viene effettuato attraverso l'asserzione assertEquals() che verifica che il valore di ritorno del metodo sia uguale al valore atteso, specificato manualmente.

Le asserzioni più importanti sono:

Asserzione Descrizione
assertEquals controlla che il valore resistuito dal metodo sia uguale al valore atteso (simile all'operatore ==)
assertStrictlyEquals controlla che il valore resistuito dal metodo sia uguale al valore atteso e dello stesso tipo (simile all'operatore ===)
assertTrue controlla che la condizione sia true
assertFalse controlla che la condizione sia false
assertNull controlla che il risultato del metodo sia null

Aggiungere un TestCase ad una TestSuite

Dopo aver sviluppato il TestCase, dobbiamo aggiungerlo alla TestSuite per eseguirlo. Dobbiamo aggiungere, nel corpo del metodo createSuite(), l'istruzione

suite.addTestSuite(MathOperationTest);

Il corpo del metodo createSuite() diventa, quindi:

private function createSuite():TestSuite {
  var suite:TestSuite = new TestSuite();
  suite.addTestSuite(MathOperationTest);
  return suite;
}

In questo modo, la suite che viene creata si preoccupa di eseguire i test della classe MathOperationTest.

Esecuzione dei test

Per eseguire i test ci basta lanciare l'applicazione che abbiamo inizialmente creato. Il risultato sarà simile a quello mostrato nella figura seguente.

Figura 4. Test superato
Test superato

Il test è, al momento, costituito da due asserzioni che vengono eseguite con successo: il test è quindi superato. Nel caso in cui durante l'esecuzione di un'asserzione si verifichi un errore, il risultato dei test sarà simile alla seguente figura

Figura 5. Errori durante il test
Errori durante il test

Nel report dei test è presente anche un dettaglio del risultato dell'esecuzione, mostrato nella figura, in cui si nota che il valore atteso (5) è diverso dal valore calcolato (6) e per questo motivo il test è fallito.

Completiamo, a questo punto, la classe MathOperationTest aggiungendo i test per gli altri metodi. Riportiamo qui una possibile implementazione della classe MathOperationTest con due asserzioni per ogni test.

I metodi setUp e tearDown

Finora, per ogni test, abbiamo istanziato un oggetto math di tipo MathOperation, necessario per l'esecuzione dei metodi. Per evitare la ripetizione di questa istruzione è possibile implementare il metodo setUp() della classe TestCase:

  • il metodo setUp() viene invocato automaticamente dal framework di testing prima dell'esecuzione di ogni test case
  • il metodo tearDown() viene invocato dopo l'esecuzione di un test case

I metodi setUp() e tearDown() sono rispettivamente utilizzati per allocare e deallocare le risorse necessarie all'esecuzione dei test case.

Possiamo, quindi, ristrutturare il codice dei test spostando la creazione dell'oggetto MathOperation nel metodo setUp(), nella cui dichiarazione dobbiamo specificare che si sta effettuando un override.

package
{
  import flexunit.framework.TestCase;
  
  public class MathOperationTest extends TestCase
  {
    public var math:MathOperation;
    
    public override function setUp():void {
      math = new MathOperation();
    }
    
    public function testAdd():void {
      assertEquals(4, math.add(1, 3));
      assertEquals(10, math.add(7, 3));
    }
    
    public function testSub():void {
      assertEquals(-2, math.sub(1, 3));
      assertEquals(4, math.sub(7, 3));
    }
    
    public function testMul():void {
      assertEquals(15, math.mul(5, 3));
      assertEquals(-21, math.mul(-7, 3));
    }
    
    public function testDiv():void {
      assertEquals(3, math.div(9, 3));
      assertEquals(3.5, math.div(7, 2));
    }
  }
} 

Conclusioni

Testare le applicazioni con un framework per lo unit testing può apparire una pratica onerosa in termini di tempo: un ulteriore lavoro durante il ciclo di sviluppo del software. In realtà, superato lo sforzo iniziale, risulta particolarmente utile testare gran parte dell'applicazione lanciando un singolo comando e poter stabilire immediatamente se una modifica ha danneggiato o meno il funzionamento dell'applicazione.

Ti consigliamo anche