I test unitari sono sempre più utilizzati per verificare la correttezza del codice che scriviamo.
Ci si trova però a volte di fronte a codice scritto in maniera poco "disaccoppiata". Questo può impedirci di sostituire a runtime dei Dependent-on Object con dei Mock Object o degli Stub. Nel talk descriverò un plugin scritto per symfony (ma utilizzabile anche in altri ambiti) che permette di sostituire delle classi a runtime ridefinendole e configurandole all'interno dei test, creando un ambiente che isola il codice da verificare.
Il talk prevederà degli esempi pratici di utilizzo dello strumento descritto.
1. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> Mocking objects practices for Symfony
2.
3.
4.
5. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> I test deveno essere il “più possibile” completi Rappresentano le nostre conoscenze (in quel momento) I test rappresentano lo stato del nostro lavoro Abbiamo introdotto errori? Niente test, niente commit. Il refactoring rende il codice pulito , comprensibile e modificabile Spaghetti code, $pippo = $k + $i??? l l Domain-driven Design rende il codice leggibile function PippoPlutoPaperino() VS function getAllButTopolino() Utilizzando il Test-Driven Development...
6.
7.
8.
9. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> Un test unitario specifica il comportamento di un metodo/una funzione o un'intera classe Un test unitario è necessariamente un test isolato Un test unitario rende esplicite le dipendenze di un metodo/oggetto rispetto ad altri metodi/oggetti Un test unitario deve prevedere la verifica di ogni comportamento dell'unità che verifica (“black box” test VS “white box” test) “ L'unità” fa la forza
10. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> Dipendenze ? Disaccoppiamento? Isolamento? Quando è difficile eseguire un test unitario
11.
12.
13. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> Legacy system/code Gli strumenti con cui lavoro (framework) mi “impongono” la struttura del codice altri??... La decisione all'atto della progettazione non mi permette di applicare i pattern che prevedono mock e stub E quando non posso modificare il codice che verifico??
14. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> Il database è spesso coinvolto E' permesso fare questo: require_once($sf_symfony_lib_dir.'/autoload/sfSimpleAutoload.class.php'); $autoload = new sfSimpleAutoload(); $autoload->addDirectory($sf_symfony_lib_dir.'/util'); $autoload->register(); E questo: new sfDatabaseManager(ProjectConfiguration::getApplicationConfiguration( 'frontend', 'test', true)); $loader = new sfPropelData(); $loader->loadData(sfConfig::get('sf_data_dir').'/fixtures'); Symfony e unit test
15. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> Lime style: include(dirname(__FILE__).'/../bootstrap/unit.php'); require_once($sf_symfony_lib_dir.'/util/sfToolkit.class.php'); class myObject { public function myMethod(){} } $t = new lime_test(16, new lime_output_color()); $t->diag('isPathAbsolute()'); $t->is(sfToolkit::isPathAbsolute('/test'), true); $t->ok(sfToolkit::setObject(new myObject()) instanceof myObject); Symfony e unit test
16. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> sfPhpunitPlugin class RepositoryTest extends PHPUnit_Framework_TestCase{ public function testGetLogFromRepository() { $proxy = $this->getMock('SvnProxy'); $xml_reader = $this->getMock('SvnXmlReader'); $logcommand = $this->getMock('SvnLogCommand'); $repository = new SVNRepository($proxy, $xml_reader); $this->assertEquals('svn log -n 5', $repository->getRevisionsLog($mock_logcommand)); } … } Symfony e unit test
17. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> idMockStubGenerator include('/Mock-Stub-Generator/FakeObjectGenerator.cless.php'); $rvm = new ReturnValuesManager('SvnLogCommand'); $rvm->setReturnValue('getSubCommandName', 'subCommandName ') ->setReturnValue('getOptionList', '--option value --option value '); FakeObjectGenerator::generate($rvm, new CodeGenerator()); $t = new lime_test(9, new lime_output_color()); $svn_proxy = new SvnProxy('file:///localrepository/newProject'); $svn_proxy->setSubCommand(new SvnLogCommand()); $t->is($svn_proxy->getCommand(), 'svn --config-dir subCommandName --option value --option value '); Symfony e unit test
18. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> Un esempio vale più di mille parole!
19. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> Non utilizzando le classi reali, come faccio ad implementare uno unit test per la mia classe SvnRepository? Lime? PHPUnit? idMockStubGenerator? Sto sviluppando un “lettore” di log di un repository SVN remoto Ho bisogno di una classe per la lettura dei file XML. Ho bisogno di una classe per la gestione del comando SVN Ho bisogno di due classi per icomandi “log” e “diff” Ho bisogno di una classe che faccia da parser per i file diff Ho bisogno di una classe che descrive i dati ricavati dai log
20. Mocking Object practices for Symfony Filippo De Santis <fd@ideato.it> Come faccio a replicare le classi “Peer” generate da propel? Lime? PHPUnit? idMockStubGenerator? Sto sviluppando due classi “user” e “book” usando Symfony e propel Ho una classe utenti Ho una classe libri Voglio cercare un libro il cui titolo è uguale al mio nome