Monthly Archives: June 2009

Come fare i test sui controller con Zend Framework

Lo ammetto, pietà di me: non sono un grandissimo esperto di test. Non ne ho mai fatti troppi e probabilmente neppure troppo belli.

Nel mio ultimo progetto, iniziato da poco, ho rimesso mano alla questione e unilateralmente deciso di applicarmi meglio a farli funzionare. Il progetto è scritto in PHP ed usa Zend Framework (da qui in avanti solo ZF). Premetto che quello che segue è probabilmente inutile a chi non ha già una certa esperienza con quel framework.

ZF è sempre stato testabile predilegendo tra i vari framework PHPUnit. Questa scelta si è spinta recentemente fino ad offrire un componente (Zend_Test) che estende le classi base dei test di PHPUnit in modo da esporre ai test dei controller tutti gli oggetti di alto livello necessari per lavorare in modo estremamente pratico. Questi oggetti sono la $request e la $response, ma anche una pletora di nuovi assert() tra cui alcuni fantastici che usano le estensioni XPath e DOM per verificare il contenuto di una pagina durante un test utilizzando appunto XPath o selettori CSS.

Quando scriveremo un test per un controller di una nostra applicazione, faremo dunque derivare il nostro test dalla classe Zend_Test_PHPUnit_ControllerTestCase.

Quelli che seguono sono consigli derivati dalla mia recente esperienza e non vogliono assolutamente essere una trattazione esaustiva sui test dei quali, come innanzi confessavo, non sono un grande esperto. Ma chi non è un grande esperto di test fa fatica a trovare documentazione di base sui test con Zend Framework.. per cui eccomi qua :)

Prima di tutto risolviamo il problema più grave: PHPUnit va installato (non viene distribuito insieme a ZF).

Si può scegliere di farlo in tre modi:

  1. via PEAR
  2. installare il pacchetto della propria distribuzione GNU/Linux
  3. andarsi a prendere direttamente il pacchetto di sorgenti dal sito.

Su Ubuntu 9.04, la mia attuale distribuzione, l’unica possibilità è la 3. Infatti la versione del pacchetto che arriva con la distribuzione è troppo vecchia e Zend_Test fa uso di feature piuttosto recenti (non ci puoi fare niente: ti beccherai subito un errore riguardante il metodo sconosciuto incrementAssertionCounter(). Show stopper.). Anche la 1 non mi è parsa percorribile a causa del mio “PEAR” troppo vecchio per la versione di PhpUnit (non ho indagato moltissimo, a dire il vero).

Ho preso dunque il tgz dal sito di PhpUnit, l’ho scompattato e ho messo tutta la sua directory PHPUnit nella stessa directory dove risiede lo ZF (directory ./library, per la cronaca). In questo modo le classi di PHPUnit sono già visibili senza che sia necessario toccare gli include path, grazie agli autoloader di ZF stesso.

Ho creato una directory “tests” allo stesso livello della directory “application” della mia applicazione, dunque fuori dalla DocumentRoot.

All’interno di questa directory ho messo, per ora: un file AllTests.php che serve come entry point per eseguire tutti i test e un file TestConfiguration.php che in pratica sostituisce le operazioni da fare nell’index.php del sito.

Ecco per esempio come potrebbe essere un primo AllTests.php:

<?php
require 'TestConfiguration.php';

$suite = new PHPUnit_Framework_TestSuite();
$suite->setName('MyApp');
$suite->addTestSuite('controllers_IndexControllerTest');
$suite->addTestSuite('controllers_UsersControllerTest');

PHPUnit_TextUI_TestRunner::run($suite, array());

Ed ecco invece parte del mio TestConfiguration.php

<?php
TestConfiguration::setUp();  

class TestConfiguration
{
  static function setUp()
  {
    define('APPLICATION_ENVIRONMENT', 'test');
    define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application/'));

    set_include_path(implode(PATH_SEPARATOR, array(
      realpath(APPLICATION_PATH . '/../library'),
      realpath(APPLICATION_PATH . '/library'),
      realpath(APPLICATION_PATH . '/models'),
      realpath(APPLICATION_PATH . '/modules/default/forms'),
      get_include_path(),
    )));

    require_once 'Zend/Loader/Autoloader.php';
    $loader = Zend_Loader_Autoloader::getInstance();
    $loader->setFallbackAutoloader(true);
  }

  static function setUpDatabase()
  {
    // TODO
  }
}  

Nella directory tests/controllers metterò i test dei controller. Per esempio, un inizio di un test per una home page potrebbe essere:

class controllers_IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{

  public function testHomePageIsASuccessfulRequest()
  {
    $this->dispatch('/');

    // Tests there are no exceptions on the home page
    $this->assertFalse($this->response->isException());

    $this->assertController('session');
    $this->assertAction('new');

    $this->assertQueryCount('#LoginForm', 1);
  }

}

A questo punto non resta che eseguire i test. Da linea comando sarà sufficiente lanciare un php AllTests.php direttamente dalla directory tests.

Un altro punto di attenzione è dovuto al fatto che PHPUnit inizia molto presto a  scrivere in console e questo potrebbe dare fastidio a particolari operazioni di ZF. Nella fattispecie mi sto riferendo al malefico errore “Headers already sent”.

Quello che succedeva a me era che in una preDispatch() di un action plugin, a fronte di una particolare situazione, effettuavo un header(“Location …”). Questa operazione non andava a buon fine proprio per il motivo di cui sopra (e tutti i test fallivano miseramente); per cui, se la vostra applicazione ha un redirect HTTP in un plugin prima dell’effettivo dispatch, potreste incorrere in un errore del genere. La soluzione (almeno per me) è stata quella di non fare la redirect ma di modificare il controller e la action della corrente request. Stesso risultato, ed anche pienamente testabile.


Altro sabato, altro portatile

E così ieri mattina, all’improvviso, il portatile di Antonella ha reso l’anima al Dio Dei Computerini. Il monitor si è fulminato.

Il portatile, un Acer 3810T Timeline 13 pollici, era stato acquistato sabato scorso e le caratteristiche sono strepitose: ultraleggero (1.6Kg) e con una batteria clamorosa (si parla di 8 ore di autonomia). Appena arrivato a casa il portatile è stato subito de-Vistato. Al posto del buffo sistema operativo della Microsoft è stata messa una Ubuntu 9.04.

Le cose non sono andate benissimo: la scheda di rete non è compatibile ed occorre scaricarsi e compilarsi un moduletto (facile). È inoltre necessario disattivare l’AHCI del disco (da BIOS, va messo in modalità IDE). Facile.

Più difficile è stato diagnosticare il motivo per il quale, ogni tanto (una volta al giorno), il sistema operativo dichiarasse di non poter scrivere sul disco rigido. Occorreva riavviare, e sperare nel successivo fsck.

Apparentemente un aggiornamento al kernel 2.6.30 aveva sistemato anche quel problema, rendendo alla fine il portatile FULL supported, per così dire. Finché ieri… il video prima ha tremato un po’ e poi… è diventato tutto bianco. Fine.

Rimpacchettato tutto, siamo andati da MediaWorld a farcelo cambiare. Nessun problema dal punto di vista della garanzia: dopo aver tentato di accenderlo il commesso ha verificato che non si vedeva nulla (e qui non nascondo che abbiamo temuto che si potesse accorgere che sulla macchina non fosse presente NESSUN sistema operativo, magari attaccando un monitor esterno. Sono sicuro che avrebbe iniziato a dare la colpa a qualche nostro intervento software (linux!) o a un cattivo aggiornamento di qualche driver).

A questo proposito non ho ancora avuto la sfortuna di sapere se e quante storie ti fanno in fatto di garanzia se sul portatile che porti a riparare dovessero trovare un altro sistema che non sia il preinstallato Windows. Per sicurezza, nel caso dell’Acer, ci siamo fatti i DVD di “ripristino” tramite l’apposita utility del fornitore.

Questa utility crea dei DVD che fanno sì boot e, ATTENZIONE, una volta partiti chiedono se si voglia ripristinare completamente il sistema: però mentono. Infatti si aspettano di trovare un file system NTFS sul nostro hard disk (dunque il ripristino è “soft”: fanno semplicemente una copia e non aggiornano neanche l’MBR; che può andare anche bene, ma almeno dammi questa opzione!). Se così non fosse daranno il classico errore del cazzo mezzo in italiano e mezzo in inglese e si fermeranno. Per cui, se si vuol essere sicuri di non andare incontro a problemi di questo tipo, i consigli possono essere:

  1. farsi comunque i DVD di ripristino (che, tra le altre cose, si possono fare UNA volta sola. Fantastici).
  2. lasciare sempre una partizione sul disco con il Windows originale (e magari anche la partizione di ripristino). Questa operazione è possibile dal momento che con il programma gparted (usato in fase di installazione di Ubuntu oppure direttamente dal LiveCD della stessa distribuzione) è possibile fare il resize della partizione NTFS. Per portare una partizione di 350GB a 35GB ci sono voluti circa 30 minuti.
  3. formattare tutto e installare la nostra Ubuntu. In caso di necessità, occorre ripartire con un LiveCD e formattare il disco come NTFS: prima si fa la partizione con gparted e poi la si formatta con mkfs.ntfs. Ricordatevi in questo caso di usare l’opzione –fast o ci passerete una vita. A questo punto si dovrebbe usare il programmillo ms-sys (non distribuito con Ubuntu) per togliere un eventuale grub nel’MBR e ripristinare quello di Vista. Ho detto “dovrebbe”, perché per motivi che non sto qui a spiegare non sono riuscito a portare a fondo questa procedura e non posso garantire il funzionamento. Occhio che a ms-sys occorre passare il device e NON la partizione! (es: /dev/sda e NON /dev/sda1 )

OK, così siamo da MediaWorld a cambiare il portatile. Peccato che tutti i MediaWorld della zona (Bologna) avessero finito le scorte di QUEL portatile e, ancora più strano, non ne erano stati ordinati altri.

Antonella ha dunque dovuto fare buon viso a cattivo gioco e “approfittare”  di una prezzo d’occasione su un portatile appena uscito: l’HP Pavilion dv3 2020el (leggi scheda). Molto più potente dell’Acer, 50€ di differenza. Nel confronto si perde un po’ di autonomia di batteria (l’HP è comunque un 6 celle) e si guadagnano circa 600gr di peso. Per il resto, però, è un’altra storia: Core2 8600 a 2,4Ghz, scheda video Nvidia con 512MB dedicati, disco da 360GB, masterizzatore interno (l’Acer non ce l’ha) e, che non guasta, una qualità componentistica certamente superiore.

Tornati in ufficio con l’acquisto, lo spacchettiamo e iniziamo la procedura pallosissima di “masterizzazione dei DVD di ripristino”. Durata totale dell’operazione: 5 ore. Cinque ore. Cinque. Ore. Per masterizzare 3 DVD. E perché? Perché (probabilmente) l’operazione è effettuabile UNA SOLA VOLTA e allora, a scapito di 5 ore di vita di un povero mortale, la procedura occorre farla in maniera estremamente cauta e lenta… Che merde.

E cosa ci troviamo su un portatile appena acquistato? 30GB di schifezze. Trentagiga di utility assurde, programmi in versione demo, giochi che devi comprare, programmi che appena li esegui si connettono a internet senza chiederti il permesso e vanno chissà dove a dire chissà cosa. Il system tray con 10 icone di altrettanti programmi che non si sa che fanno. Gli antivirus che ogni 5 minuti appaiono e ti chiedono o di acquistare il prodotto o di far riavviare la macchina. E in tutto questo, per far partire IE7 per passare un po’ il tempo, ci vogliono svariati secondi prima di atterrare… sulla home page di AOL!?

Ma come si fa, dico,  a usare e legittimare l’uso di un sistema del genere? (e per sistema non intendo “Windows” in sé, quanto proprio questa cafonaggine da parte dei produttori).

Che roba. Bah! Chiavetta USB e dopo 30 minuti di NTFS resize e 10 minuti di installazione Ubuntu era lì pronta.

Problemi? Solo l’audio: occorre aggiornare manualmente Alsa alla versione 1.0.20.

Per ora non posso dire altro che con Ubuntu l’HP Pavilion dv3 2020el è una bomba. Con Windows è una roba ridicola e ributtante, come tutti i portatili odierni.


I contenuti di questo sito sono distribuiti con una licenza Creative Commons 2.5 eccetto dove diversamente specificato.

Tema WordPress Punto5 sviluppato da Claudio Cicali; icone del set famfamfam silk e komodomedia.

© 2005-2010
Claudio Cicali