Post on 08-May-2015
description
© All rights reserved. Zend Technologies, Inc.
Il testing con Zend FrameworkEnrico ZimuelSenior Consultant & ArchitectZend Technologies
© All rights reserved. Zend Technologies, Inc.
Sommario● Introduzione allo Unit Testing in PHP● Le funzionalità di base di test con ZF● Alcuni scenari avanzati di testing con ZF
© All rights reserved. Zend Technologies, Inc.
Perchè testare il codice?
© All rights reserved. Zend Technologies, Inc.
Semplificare la manutenzione● Il testing definisce le aspettative● Il testing descrive i comportamenti
dell'applicazione● Il testing identifica i cambiamenti nel
codice sorgente che violano (“rompono”) i comportamenti attesi del software
© All rights reserved. Zend Technologies, Inc.
Quantificare la qualità del codice● Copertura del codice (code coverage)
effettuata dagli strumenti di testing● I metodi di test documentano i
comportamenti attesi del software
6 © All rights reserved. Zend Technologies, Inc.
Benefici psicologici per gli sviluppatori
Quando il test è ok, gli sviluppatori sono piùconfidenti e motivati!
7 © All rights reserved. Zend Technologies, Inc.
Testing non è … ricaricare una pagina
8 © All rights reserved. Zend Technologies, Inc.
Testing non è … var_dump()
9 © All rights reserved. Zend Technologies, Inc.
Testing è … riproducibile
10 © All rights reserved. Zend Technologies, Inc.
Testing è … automatizzabile
© All rights reserved. Zend Technologies, Inc.
In un buon testing …● Si definiscono i comportamenti● Si forniscono esempi su scenari d'utilizzo● Si definiscono le aspettative
© All rights reserved. Zend Technologies, Inc.
PHP testing frameworks● PHPT
▶ Utilizzato da PHP, da PEAR e da alcune librerie indipendenti
● SimpleTest▶ Un framework di testing in stile JUnit
● PHPUnit▶ Un framework di testing in stile JUnit▶ De facto lo standard di testing in PHP
13 © All rights reserved. Zend Technologies, Inc.
Le basi del testing
© All rights reserved. Zend Technologies, Inc.
Scrivere unit test● Creare una classe di test
● Creare uno o più metodi che definiscono dei comportamenti
▶ Descrivere il comportamento in un linguaggio naturale
● Scrivere codice che definisce il comportamento
▶ Scrivere codice utilizzando l'API● Scrivere le asserzioni per definire il
comportamento atteso
© All rights reserved. Zend Technologies, Inc.
Creare una classe di test● Solitamente il nome termina per Test
class EntryTest extends PHPUnit_Framework_TestCase{}
class EntryTest extends PHPUnit_Framework_TestCase{}
© All rights reserved. Zend Technologies, Inc.
Scrivere un metodo che definisce il comportamento
● prefisso “test”
class EntryTest extends PHPUnit_Framework_TestCase{ public function testMaySetTimestampWithString() { }}
class EntryTest extends PHPUnit_Framework_TestCase{ public function testMaySetTimestampWithString() { }}
© All rights reserved. Zend Technologies, Inc.
Scrivere il codice per il comportamento
class EntryTest extends PHPUnit_Framework_TestCase{ public function testMaySetTimestampWithString() { $string = 'Fri, 7 May 2010 09:26:03 -0700'; $ts = strtotime($string); $this->entry->setTimestamp($string); $setValue = $this->entry->getTimestamp(); }}
class EntryTest extends PHPUnit_Framework_TestCase{ public function testMaySetTimestampWithString() { $string = 'Fri, 7 May 2010 09:26:03 -0700'; $ts = strtotime($string); $this->entry->setTimestamp($string); $setValue = $this->entry->getTimestamp(); }}
© All rights reserved. Zend Technologies, Inc.
Scrivere asserzioni per un comportamento atteso
class EntryTest extends PHPUnit_Framework_TestCase{ public function testMaySetTimestampWithString() { $string = 'Fri, 7 May 2010 09:26:03 -0700'; $ts = strtotime($string); $this->entry->setTimestamp($string); $setValue = $this->entry->getTimestamp(); $this->assertSame($ts, $setValue); }}
class EntryTest extends PHPUnit_Framework_TestCase{ public function testMaySetTimestampWithString() { $string = 'Fri, 7 May 2010 09:26:03 -0700'; $ts = strtotime($string); $this->entry->setTimestamp($string); $setValue = $this->entry->getTimestamp(); $this->assertSame($ts, $setValue); }}
© All rights reserved. Zend Technologies, Inc.
Eseguire il test● Fallimento?
▶ Verifica il test e le asserzioni per eventuali errori di battitura o casi d'uso
▶ Verifica la classe che si stà testando▶ Eseguire le correzioni e rilanciare il test
● Successo?▶ Creare il prossimo test di comportamento
o continuare con le modifiche sul codice del software
20 © All rights reserved. Zend Technologies, Inc.
Alcuni termini del testing
© All rights reserved. Zend Technologies, Inc.
Test scaffolding● Essere sicuri che l'ambiente di testing sia
libero da pre-requisiti● Inizializzare le dipendenze necessarie per
eseguire il test● Di solito l'inizializzazione dell'ambiente di
test avviene nel metodo setUp()
© All rights reserved. Zend Technologies, Inc.
Test doubles● Stubs
Sostituire un oggetto con un altro per continuare il test
● Mock ObjectsSostituire un oggetto con un altro forzandone le aspettative (restituendo valori prestabiliti per i metodi)
© All rights reserved. Zend Technologies, Inc.
Alcune tipologie di test● Testing condizionali
Testing solo al verificarsi di alcune condizioni d'ambiente
● Testing funzionali e d'integrazioneTesting del sistema per verificare i comportamenti attesi; testing delle unità e delle loro interazioni
24 © All rights reserved. Zend Technologies, Inc.
Testing semi-funzionalein Zend Framework
© All rights reserved. Zend Technologies, Inc.
Fasi principali● Setup dell'ambiente phpUnit● Creare uno scenario di test (TestCase)
basato su un Controller (ControllerTestCase)
● Bootstrap dell'applicazione● Creazione di una richiesta e dispatch● Eseguire asserzioni sulle risposte
© All rights reserved. Zend Technologies, Inc.
L'ambiente PHPUnit● Struttura delle directory
tests|-- application| `-- controllers|-- Bootstrap.php|-- library| `-- Custom`-- phpunit.xml
4 directories, 2 files
tests|-- application| `-- controllers|-- Bootstrap.php|-- library| `-- Custom`-- phpunit.xml
4 directories, 2 files
© All rights reserved. Zend Technologies, Inc.
L'ambiente PHPUnit (2)● phpunit.xml
<phpunit bootstrap="./Bootstrap.php"> <testsuite name="Test Suite"> <directory>./</directory> </testsuite> <filter> <whitelist> <directory suffix=".php">../library/</directory> <directory suffix=".php">../application/</directory> <exclude> <directory suffix=".phtml">../application/</directory> </exclude> </whitelist> </filter></phpunit>
<phpunit bootstrap="./Bootstrap.php"> <testsuite name="Test Suite"> <directory>./</directory> </testsuite> <filter> <whitelist> <directory suffix=".php">../library/</directory> <directory suffix=".php">../application/</directory> <exclude> <directory suffix=".phtml">../application/</directory> </exclude> </whitelist> </filter></phpunit>
© All rights reserved. Zend Technologies, Inc.
L'ambiente PHPUnit (3)● Bootstrap.php
$rootPath = realpath(dirname(__DIR__));if (!defined('APPLICATION_PATH')) { define('APPLICATION_PATH', $rootPath . '/application');}if (!defined('APPLICATION_ENV')) { define('APPLICATION_ENV', 'testing');}set_include_path(implode(PATH_SEPARATOR, array( '.', $rootPath . '/library', get_include_path(),)));require_once 'Zend/Loader/Autoloader.php';$loader = Zend_Loader_Autoloader::getInstance();$loader->registerNamespace('Custom_');
$rootPath = realpath(dirname(__DIR__));if (!defined('APPLICATION_PATH')) { define('APPLICATION_PATH', $rootPath . '/application');}if (!defined('APPLICATION_ENV')) { define('APPLICATION_ENV', 'testing');}set_include_path(implode(PATH_SEPARATOR, array( '.', $rootPath . '/library', get_include_path(),)));require_once 'Zend/Loader/Autoloader.php';$loader = Zend_Loader_Autoloader::getInstance();$loader->registerNamespace('Custom_');
© All rights reserved. Zend Technologies, Inc.
Creare una classe di test● Estendere la Zend_Test_PHPUnit_ControllerTestCase
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{}
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{}
© All rights reserved. Zend Technologies, Inc.
Bootstrap dell'applicazione● Creare un'istanza di Zend_Application e
referenziarla nel setUp()
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ public function setUp() { $this->bootstrap = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini' ); parent::setUp(); }}
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ public function setUp() { $this->bootstrap = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini' ); parent::setUp(); }}
© All rights reserved. Zend Technologies, Inc.
Creazione e dispatch di una richiesta
● Metodo semplice: dispatch di una “url”
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ // ... public function testStaticPageHasGoodStructure() { $this->dispatch('/example/page'); // ... }}
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ // ... public function testStaticPageHasGoodStructure() { $this->dispatch('/example/page'); // ... }}
© All rights reserved. Zend Technologies, Inc.
● Avanzato: personalizzare l'oggetto della richiesta prima di eseguire il dispatchclass ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ // ... public function testXhrRequestReturnsJson() { $this->getRequest() ->setHeader('X-Requested-With', 'XMLHttpRequest') ->setQuery('format', 'json'); $this->dispatch('/example/xhr-endpoint'); // ... }}
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ // ... public function testXhrRequestReturnsJson() { $this->getRequest() ->setHeader('X-Requested-With', 'XMLHttpRequest') ->setQuery('format', 'json'); $this->dispatch('/example/xhr-endpoint'); // ... }}
Creazione e dispatch di una richiesta (2)
© All rights reserved. Zend Technologies, Inc.
Creare asserzioni● Tipiche asserzioni:
▶ Verifica della struttura della risposta e dei markup Utilizzando selettori CSS o query XPath
▶ Verificare il codice della risposta HTTP o l'header di pagina
▶ Verificare artefatti sulla richiesta e sulla risposta
© All rights reserved. Zend Technologies, Inc.
Asserzioni con selettori CSS ● assertQuery($path, $message = '')
● assertQueryContentContains( $path, $match, $message = '')
● assertQueryContentRegex( $path, $pattern, $message = '')
● assertQueryCount($path, $count, $message = '')
● assertQueryCountMin($path, $count, $message = '')
● assertQueryCountMax($path, $count, $message = '')
● ognuna ha una variante "Not"
© All rights reserved. Zend Technologies, Inc.
Asserzioni con selettori XPath● assertXpath($path, $message = '')
● assertXpathContentContains( $path, $match, $message = '')
● assertXpathContentRegex( $path, $pattern, $message = '')
● assertXpathCount($path, $count, $message = '')
● assertXpathCountMin($path, $count, $message = '')
● assertXpathCountMax($path, $count, $message = '')
● ognuna ha una variante "Not"
© All rights reserved. Zend Technologies, Inc.
Asserzioni su Redirect● assertRedirect($message = '')
● assertRedirectTo($url, $message = '')
● assertRedirectRegex($pattern, $message = '')
● ognuna ha una variante "Not"
© All rights reserved. Zend Technologies, Inc.
Asserzioni sulle risposte● assertResponseCode($code, $message = '')
● assertHeader($header, $message = '')
● assertHeaderContains($header, $match, $message = '')
● assertHeaderRegex($header, $pattern, $message = '')
● ognuna ha una variante "Not"
© All rights reserved. Zend Technologies, Inc.
Asserzioni sulle richieste● assertModule($module, $message = '')
● assertController($controller, $message = '')
● assertAction($action, $message = '')
● assertRoute($route, $message = '')
● ognuna ha una variante "Not"
© All rights reserved. Zend Technologies, Inc.
Esempi di asserzioni
public function testSomeStaticPageHasGoodStructure(){ $this->dispatch('/example/page'); $this->assertResponseCode(200); $this->assertQuery('div#content p'); $this->assertQueryCount('div#sidebar ul li', 3);}
public function testSomeStaticPageHasGoodStructure(){ $this->dispatch('/example/page'); $this->assertResponseCode(200); $this->assertQuery('div#content p'); $this->assertQueryCount('div#sidebar ul li', 3);}
© All rights reserved. Zend Technologies, Inc.
Esempi di asserzioni (2)
public function testXhrRequestReturnsJson(){ // ... $this->assertNotRedirect(); $this->assertHeaderContains( 'Content-Type', 'application/json');}
public function testXhrRequestReturnsJson(){ // ... $this->assertNotRedirect(); $this->assertHeaderContains( 'Content-Type', 'application/json');}
41 © All rights reserved. Zend Technologies, Inc.
Alcuni casi avanzati di testing
© All rights reserved. Zend Technologies, Inc.
Testing di modelli e risorse● Problema:
Queste classi non vengono caricate con il sistema di autoloading
● Soluzione:Utilizzare il bootstrap di Zend_Application per caricare manualmente le risorse durante il setUp()
© All rights reserved. Zend Technologies, Inc.
Esempio
class Blog_Model_EntryTest extends PHPUnit_Framework_TestCase{ public function setUp() { $this->bootstrap = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini' ); $this->bootstrap->bootstrap('modules');
$this->model = new Blog_Model_Entry(); }
}
class Blog_Model_EntryTest extends PHPUnit_Framework_TestCase{ public function setUp() { $this->bootstrap = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini' ); $this->bootstrap->bootstrap('modules');
$this->model = new Blog_Model_Entry(); }
}
© All rights reserved. Zend Technologies, Inc.
Testing con autenticazione● Problema:
Alcune azioni possono richiedere un'autenticazione utente, come emularla in fase di testing?
● Soluzione:Eseguire un'autenticazione manuale utilizzando Zend_Auth prima di eseguire il dispatch()
© All rights reserved. Zend Technologies, Inc.
Esempio
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ // ... public function loginUser($user) { $params = array('user' => $user); $adapter = new Custom_Auth_TestAdapter( $params); $auth = Zend_Auth::getInstance(); $auth->authenticate($adapter); $this->assertTrue($auth->hasIdentity()); }}
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ // ... public function loginUser($user) { $params = array('user' => $user); $adapter = new Custom_Auth_TestAdapter( $params); $auth = Zend_Auth::getInstance(); $auth->authenticate($adapter); $this->assertTrue($auth->hasIdentity()); }}
© All rights reserved. Zend Technologies, Inc.
Esempio (2)
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ // ... public function testAdminUserCanAccessAdmin() { $this->loginUser('admin'); $this->dispatch('/example/admin'); $this->assertQuery('div#content.admin'); }
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ // ... public function testAdminUserCanAccessAdmin() { $this->loginUser('admin'); $this->dispatch('/example/admin'); $this->assertQuery('div#content.admin'); }
© All rights reserved. Zend Technologies, Inc.
Testing di pagine che dipendono da altre azioni● Problema:
Alcune azioni possono dipendere dall'esito di altre, ad esempio una pagina che evidenzia I risultati di un'operazione di ricerca
● Soluzione:Eseguire un doppio dispatch, resettando la richiesta tra una chiamata e l'altra
© All rights reserved. Zend Technologies, Inc.
Esempio
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ // ... public function testHighlightedTextAfterSearch() { $this->getRequest()->setQuery( 'search', 'foobar'); $this->dispatch('/search');
$this->resetRequest(); $this->resetResponse();
$this->dispatch('/example/page'); $this->assertQueryContains( 'span.highlight', 'foobar'); }
class ExampleControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ // ... public function testHighlightedTextAfterSearch() { $this->getRequest()->setQuery( 'search', 'foobar'); $this->dispatch('/search');
$this->resetRequest(); $this->resetResponse();
$this->dispatch('/example/page'); $this->assertQueryContains( 'span.highlight', 'foobar'); }
49 © All rights reserved. Zend Technologies, Inc.
Conclusioni
© All rights reserved. Zend Technologies, Inc.
Eseguire sempre il test!● Test dei modelli, dei livelli di servizio, etc● Eseguire test funzionali e di accettazione
per il workflow dell'applicazione, per la struttura delle pagine, etc
● Testing = Scrivere codice migliore, più affidabile e di qualità
© All rights reserved. Zend Technologies, Inc.
Grazie!
Per maggiori informazioni:http://www.zend.comhttp://framework.zend.com