2. TDD, un breve riepilogo
Test Driven Development: metodologia di sviluppo
software che antepone la stesura di test allo sviluppo
reale delle funzionalità.
Come?
Scrivo test che
fallisce e lo
eseguo
Rifattorizzo il
codice
Scrivo codice che
soddisfi il test
3. TDD, un breve riepilogo
Perché scrivere prima il test?
Previene/fa regredire/mette in luce bug;
Assicura copertura del codice;
Permette di comprendere finalità e modalità della
funzione testata;
Permette refactoring, sperimentazioni e
cambiamenti nel codice preesistente
4. TDD, un breve riepilogo
Come deve essere un buon test?
Automatizzato;
Isolato;
Ripetibile;
Facile da scrivere, leggere => facile da mantenere
5. Fasi di un test
SUT: system under test (classe, oggetto o metodo testato)
6. Fasi di un test
VS metodo scientifico
Ipotesi
Tesi
Dimostrazione
7. Test classico 1/2
class OrderTest extends PHPUnit_Framework_TestCase {
const DISARONNO = ‘Amaretto di Saronno’;
const BACARDI = ‘Bacardi Superior’;
private $warehouse;
public function setUp()
{
$this->warehouse = new Warehouse();
$this->warehouse->add(self::DISARONNO, 50);
$this->warehouse->add(self::BACARDI, 25);
}
public function testFillingRemovesInventoryIfInStock()
{
$order = new Order(self::DISARONNO, 50); //fase setup II parte
$order->fill($this->warehouse); //fase exercise
$this->assertTrue($order->isFilled()); //fase verification
$this->assertCount(0, $this->warehouse->getInventory(self::DISARONNO));
}
8. Test classico 2/2
public function testFillingRemovesInventoryIfInStock()
{
$order = new Order(self::DISARONNO, 51);
$order->fill($this->warehouse);
$this->assertFalse($order->isFilled());
$this->assertCount(50, $this->warehouse->getInventory(self::DISARONNO));
}
}
10. Test Double, quando entrano in gioco?
Indirect output
Indirect input
DOC: depended on component
11. Test Double, quando entrano in gioco?
Indirect output
Indirect input
Il Test Double sostituisce il DOC in fase di test
12. Test Double, varianti
Dummy Object: un placeholder passato al SUT ma
mai utilizzato
Test Stub: sostituisce un DOC per avere un punto di
controllo sugli indirect input necessari al SUT
Test Spy: offre un punto di osservazione sugli
indirect input necessari al SUT
Mock Object: costituisce un punto di osservazione
sugli indirect output erogati dal SUT
Fake Object: sostituisce le funzionalità di un DOC
con una implementazione semplificata senza fornire
controllo sugli indirect input/output del SUT
13. Test Double – Mock 1/2
class OrderTest extends PHPUnit_Framework_TestCase
{
const DISARONNO = ‘Amaretto di Saronno’;
public function testFillingRemovesInventoryIfInStock()
{
//setup - dati
$order = new Order(self::DISARONNO, 50);
$warehouseMock = $this->getMock('Warehouse', array('hasInventory', 'remove'));
$warehouseMock->expects($this->at(0)) //setup - expectations
->method('hasInventory')
->with($this->equalTo(self::DISARONNO), $this->equalTo(50)) //indirect output
->will($this->returnValue(true)); //indirect input
$order->fill($warehouseMock); //exercise
14. Test Double – Mock 2/2
$this->assertTrue($order->isFilled()); //verify
}
public function testFillingDoesNotRemoveIfNotEnoughInStock() {
$order = new Order(self::DISARONNO, 51);
$warehouseMock = $this->getMock('Warehouse', array('hasInventory'));
$warehouseMock->expects($this->once())
->method("hasInventory”)
->will($this->returnValue(false));
$order->fill($warehouseMock);
$this->assertFalse($order->isFilled());
}
}
15. Test Double – Stub 1/2
interface MailService {
public function send (Message $msg);
}
class MailServiceStub implements MailService {
private $messages = array();
public function send (Message $msg)
{
$this->messages[] = $msg;
}
public function numberSent()
{
return count($this->messages);
}
}
16. Test Double – Stub 2/2
class OrderTest extends PHPUnit_Framework_TestCase
{
…
public function testOrderSendsMailIfUnfilled() {
$order = new Order(self::DISARONNO, 51);
...
$mailServiceStub = new MailServiceStub();
$order->setMailer($mailServiceStub);
$order->fill($warehouse);
$this->assertEquals(1, $mailer->numberSent());
}
}
17. Test Double – Stub vs Mock as Spy
class OrderTest extends PHPUnit_Framework_TestCase
{
…
public function testOrderSendsMailIfUnfilled() {
$order = new Order(self::DISARONNO, 51);
…
$mailServiceMock = $this->getMock('MailService', array('send'));
$order->setMailer($mailServiceMock);
$mailServiceMock->expects($this->once())
->method("send");
$order->fill($warehouse);
}
}
18. Credits & Contacts
PHPUnit – manuale - http://phpunit.de/manual/
xUnit Patterns – Test Double, Gerard Meszaros -
http://xunitpatterns.com/Test%20Double.html
Mocks arent Stubs, Martin Fowler -
http://martinfowler.com/articles/mocksArentStubs.html
Test Double, un’introduzione
di Carmelantonio Zolfo
carmelantonio.zolfo@gmail.com