Il web test

SimpleTest for PHP web script testing documentation

# fetch Recuperare una pagina

Il collaudo delle classi è bello ed utile ma PHP è principalmente un linguaggio per sviluppare funzionalità dentro pagine web. Com'è possibile collaudare il layer di presentazione di un'applicazione PHP? In fin dei conti, le pagine web non sono altro che stringhe di testo e perciò si dovrebbe essere in grado di collaudarle esattamente come ogni altro dato di test.

Questo introduce un problema delicato. Se il collaudo avvenisse ad un livello troppo basso, il dover verificare ogni singolo tag della pagina con dei pattern, ad esempio, condurrebbe a dei test troppo fragili. La più insignificante modifica nel layout potrebbe compromettere il funzionamento di un gran numero di test. Del resto, se si collaudasse ad un livello troppo alto, ad esempio utilizzando una versione mock del template engine, si perderebbe la possibilità di collaudare alcune classi del test. Per esempio, l'interazione tra i form e la navigazione dovrebbe essere collaudata manualmente. Queste tipologie di test sono estremamente ripetitive e soggette ad errori.

SimpleTest include una speciale forma di test case per il collaudo delle azioni sulle pagine web. `WebTestCase` comprende facilitazioni per la navigazione, per la verifica del contenuto delle pagine e dei cookie e per la manipolazione di form. L'uso dei test case web è simile a quello di UnitTestCase: <strong>class TestOfLastcraft extends WebTestCase { }</strong> Qui si sta per eseguire il test del sito Last Craft stesso. Il test è ospitato in un file chiamato lastcraft_test.php che può essere caricato da uno script proprio come gli unit test: <?php require_once('simpletest/autorun.php');<strong> require_once('simpletest/web_tester.php');</strong> SimpleTest::prefer(new TextReporter()); class WebTests extends TestSuite { function WebTests() { $this->TestSuite('Web site tests');<strong> $this->addFile('lastcraft_test.php');</strong> } } ?> Si sta usando il text reporter per distinguere più chiaramente il contenuto della pagina web dall'output testuale.

Non è ancora stato definito alcun test. E' possibile recuperare la home page con il metodo `get()`: class TestOfLastcraft extends WebTestCase { <strong> function testHomepage() { $this->assertTrue($this->get('http://www.lastcraft.com/')); }</strong> } Il metodo `get()` restituisce true solo se il contenuto della pagina è stato correttamente caricato. E' un modo semplice ma rozzo per verificare che una pagina web sia effettivamente restituita dal web server. Anche nel caso il contenuto della pagina dovesse essere un messaggio di errore 404 il metodo `get()` continuerebbe a restituire true.

Supponiamo che il web server di Last Craft sia attivo (sfortunatamente non è sempre così), si vedrebbe:

    Web site tests
    OK
    Test cases run: 1/1, Failures: 0, Exceptions: 0

Tutto quello che si è verificato, in sostanza, è che il web server restituisca una qualsiasi pagina ma ancora non sappiamo se sia quella corretta.

# content Collaudare il contenuto della pagina

Per garantire che la pagina sulla quale ci si trovi sia quella prevista, abbiamo bisogno di verificarne il contenuto. class TestOfLastcraft extends WebTestCase { function testHomepage() {<strong> $this->get('http://www.lastcraft.com/'); $this->assertText('Why the last craft');</strong> } } La pagina prelevata precedentemente viene conservata in un buffer del test case perciò non c'è la necessità di referenziarla direttamente. Il pattern match viene sempre eseguito sul contenuto del buffer.

Di seguito è riportato un elenco dei possibili assert: Here is the list of possible content assertions...

`assertTitle($title)` | Passa se il titolo corrisponde esattamente
`assertText($text)` | Passa se un testo visibile o un "alt" corrisponde al pattern
`assertNoText($text)` | Passa se un testo visibile o un "alt" non corrisponde al pattern
`assertPattern($pattern)` | Esegue un confronto tra il contenuto della pagina ed un pattern Perl
`assertNoPattern($pattern)` | Passa se un pattern Perl fallisce sul contenuto della pagina
`assertLink($label)` | Passa se viene trovato un link contenente il testo specificato
`assertNoLink($label)` | Passa se non viene trovato un link contenente il testo specificato
`assertLinkById($id)` | Passa se viene trovato un link con l'attributo id specificato
`assertNoLinkById($id)` | Passa se non viene trovato alcun link con l'attributo id specificato
`assertField($name, $value)` | Passa se viene trovato un tag input con l'attributo name specificato
`assertFieldById($id, $value)` | Passa se viene trovato un tag input con l'attributo id specificato
`assertResponse($codes)` | Passa se la risposta HTTP corrisponde all'elenco specificato
`assertMime($types)` | Passa se viene trovato un MIME type dell'elenco
`assertAuthentication($protocol)` | Passa se è stato utilizzato il protocollo di autenticazione specificato
`assertNoAuthentication()` | Passa se non è stato utilizzato il protocollo di autenticazione specificato
`assertRealm($name)` | Pass if the current challenge realm matches
`assertHeader($header, $content)` | Passa se c'è la corrispondenza con un campo dell'header
`assertNoHeader($header)` | Passa se non c'è la corrispondenza con un campo dell'header
`assertCookie($name, $value)` | Passa se viene trovato il cookie specificato
`assertNoCookie($name)` | Passa se non viene trovato un cookie con il nome specificato

Come le altre assert di SimpleTest, queste restituiscono false in caso di fallimento e true in caso di successo. E' anche permesso un parametro opzionale con un messaggio nel quale è possibile includere il messaggio di test originale per mezzo della stringa "%s".

Così, è possibile eseguire verifiche sul tag title con: <strong>$this->assertTitle('The Last Craft? Web developer tutorials on PHP, Extreme programming and Object Oriented development');</strong> o, se queto fosse troppo fragile, con: <strong>$this->assertTitle(new PatternExpectation('/The Last Craft/'));</strong> Così come si può verificare il semplice contenuto HTML, si può verificare che il MIME type sia compreso in un elenco di MIME type consentiti, con: <strong>$this->assertMime(array('text/plain', 'text/html'));</strong> Più interessante è il collaudo del codice di risposta HTTP. Come per il MIME type, si può verificare che il codice di risposta sia incluso in un elenco di valori consentiti: class TestOfLastcraft extends WebTestCase { function testRedirects() { $this->get('http://www.lastcraft.com/test/redirect.php'); $this->assertResponse(200);</strong> } } Qui si sta verificando che il prelievo della pagina si sia concluso con successo consentendo solo risposte con codice HTTP 200. Anche se questo testo passa non è esattamente pensato in modo corretto. Se la pagina richiesta non esistesse ed il server eseguisse un redirect,

`WebTestCase` seguirebbe automaticamente il redirect. I test così pensati sono più robusti dal momento che, di solito, si è interessati all'interazione con le pagine piuttosto che al modo in cui queste vengono consegnate.

Se l'esecuzione di un redirect è di nostro interesse, allora possiamo sempre disabilitare questa caratteristica: class TestOfLastcraft extends WebTestCase { function testHomepage() {<strong> $this->setMaximumRedirects(0);</strong> $this->get('http://www.lastcraft.com/test/redirect.php'); $this->assertResponse(200); } } Adesso questa assert fallisce come richiesto:

    Web site tests
    1) Expecting response in [200] got [302]
        in testhomepage
        in testoflastcraft
        in lastcraft_test.php
    FAILURES!!!
    Test cases run: 1/1, Failures: 1, Exceptions: 0

Possiamo modificare il test per accettare anche i redirect così: class TestOfLastcraft extends WebTestCase { function testHomepage() { $this->setMaximumRedirects(0); $this->get('http://www.lastcraft.com/test/redirect.php'); $this->assertResponse(<strong>array(301, 302, 303, 307)</strong>); } } Adesso il test passa.

# navigation Navigare un sito durante il test

Normalmente gli utenti non navigano il sito scrivendo gli URL, ma cliccando su link e pulsanti. Qui si verifica che la pagina con i contatti possa essere raggiunta dalla home page: class TestOfLastcraft extends WebTestCase { ... function testContact() { $this->get('http://www.lastcraft.com/');<strong> $this->clickLink('About'); $this->assertTitle(new PatternExpectation('/About Last Craft/'));</strong> } } Il parametro è il testo del link.

Se l'oggetto da collaudare fosse un bottone piuttosto che un anchor tag, allora è possibile usare `clickSubmit()` sul pulsante specificato: <strong>$this->clickSubmit('Go!');</strong> Se non si è sicuri o non si vuole specificare il caso, come accade di solito, è sufficiente usare il metodo `click()`: <strong>$this->click('Go!');</strong>

L'elenco dei metodi di navigazione è:

`getUrl()` | Restituisce la posizione corrente
`get($url, $parameters)` | Invia una richiesta GET con i parametri specificati
`post($url, $parameters)` | Invia una richiesta POST con i parametri specificati
`head($url, $parameters)` | Invia una richiesta HEAD senza sostituire il contenuto della pagina
`retry()` | Ripete l'ultima richiesta
`back()` | Si comporta come il pulsante Indietro del browser
`forward()` | Si comporta come il pulsante Avanti del browser
`authenticate($name, $password)` | Riprova un tentativo di autenticazione
`restart()` | Riavvia il browser con una nuova sessione
`getCookie($name)` | Recupera il valore di un cookie dal contesto corrente
`ageCookies($interval)` | fa invecchiare i cookie della sessione prima di un riavvio
`clearFrameFocus()` | Go back to treating all frames as one page
`clickSubmit($label)` | Preme il primo button che abbia la label specificata
`clickSubmitByName($name)` | Preme il pulsante con il nome specificato
`clickSubmitById($id)` | Preme il pulsante con l'id specificato
`clickImage($label, $x, $y)` | Preme sul tag input di tipo image con il titolo o il tag alt specificato
`clickImageByName($name, $x, $y)` | Preme sul tag input di tipo image con il nome specificato
`clickImageById($id, $x, $y)` | Preme sul tag input di tipo image con l'id specificato
`submitFormById($id)` | Esegue il submit di un form senza inviare il valore di submit
`clickLink($label, $index)` | Preme l'anchor che abbia la label specificata
`clickLinkById($id)` | Preme l'anchor che abbia l'id specificato
`getFrameFocus()` | Restituisce il ome del frame correntemente selezionato
`setFrameFocusByIndex($choice)` | Seleziona il frame specificato, contando i frame a partire da 1
`setFrameFocus($name)` | Seleziona il frame con il nome specificato

I parametri dei metodi `get()`, `post()` e `head()` sono opzionali. Il recupero dell'HEAD HTTP non modifica il contesto del browser ma si limita a caricare i cookie. Questo può risultare utile quando un'immagine o uno stylesheet imposta un cookie per bloccare i robot più astuti.

I comandi `retry()`, `back()` e `forward()` funzionano esattamente come i corrispondenti comandi del web browser ed utilizzano la history per recuperare le pagine. Questo può essere molto comodo quando si voglia verificare l'effetto della pressione del pulsante Indietro nei form.

C'è bisogno di un piccolo chiarimento a proposito dei metodi per i frame. Di default, una pagina all'interno di un frame viene trattata esattamente come tutte le altre. Le ricerche vengono effettuate su tutto il frameset e quindi la pressione di un link funzionerà indipendentemente dal frame che contiene il tag. E' possibile sovrascrivere questo comportamento spostando il focus su un singolo frame. Così facendo, tutte le ricerche e le azioni come le autenticazioni e i nuovi tentativi di esecuzione verranno applicate solo al frame selezionato: se i link ed i pulsanti non si trovano nel frame selezionato non possono essere premuti.

Il collaudo della navigazione di pagine statiche fornisce solo indicazioni di malfunzionamenti nell'intero script. Per pagine altamente dinamiche, come ad esempio i bulletin board, questo può essere cruciale la verifica della correttezza dell'applicazione. Tuttavia, per molte applicazioni, la parte veramente delicata della logica si trova nella gestione di form e sessioni. Fortunatamente SimpleTest include anche degli appositi tool per il collaudo di form.

# request Modifiche alla richiesta e metodi di debug

Nonostante SimpleTest non abbia tra i suoi obiettivi quello di permettere il collaudo dei problemi di rete, comprende anche alcuni metodi per modificare e debuggare le richieste effettuate. Di seguito un elenco dei metodi disponibili:

`getTransportError()` | Restituisce l'ultimo errore del socket
`showRequest()` | Esegue il dump della richiesta inviata
`showHeaders()` | Esegue il dump dell'header ricevuto
`showSource()` | Esegue il dump del contenuto HTML
`ignoreFrames()` | Evita il caricamento del frameset
`setCookie($name, $value)` | Imposta un cookie a partire dall'istante corrente
`addHeader($header)` | Aggiunge sempre l'header specificato alla richiesta
`setMaximumRedirects($max)` | Interrompe il test dopo il numero di richieste specificate
`setConnectionTimeout($timeout)` | termina la connessione dopo l'intervallo specificato
`useProxy($proxy, $name, $password)` | Esegue le richieste tramile il proxy URL specificato

Questi metodi sono utilizzati principalmente per debugging.
Recuperare una pagina Collaudare il contenuto di una pagina Navigare un sito durante il test Modifiche alla richiesta e metodi di debug
SimpleTest project page on SourceForge. SimpleTest download page on LastCraft. The developer's API for SimpleTest gives full detail on the classes and assertions available.
software development, php programming for clients, customer focused php, software development tools, acceptance testing framework, free php scripts, architecture, php resources, HTMLUnit, JWebUnit, php testing, unit test resource, web testing