TDD, a starting point
A PHP perspective...
Francesco Fullone, Ideato.it
ff AT ideato.it
Who am I

Francesco Fullone aka Fullo

- PHP developer since 1999
-             President
-      and Open Source Evangelist
- CEO @


- Nerd and geek
TDD ?
Test Driven
Development
Why test the code?
Developers are humans,
Humans are error-prone.
Software changes
    and grows.
We need to confirm that
 the code is working
  after any changes.
xUNIT TDD approach.
PHPUnit.de
Easy to write
Easy to read
  Isolated
Composable
An example:
The bowling kata
  (courtesy grabbed from Sebastian Bergmann)
10 frames

2 rolls to knock down the 10 pins

Score for a frame is the number of pins
knocked down

Bonus for a spare (all 10 pins knocked down in
two tries): next roll

Bonus for a strike (all 10 pins knocked down in
one try): next two rolls

Extra rolls for spare or strike in the 10th frame
<?php
require_once 'BowlingGame.php';

class BowlingGameTest extends PHPUnit_Framework_TestCase
{




}
?>
<?php
require_once 'BowlingGame.php';

class BowlingGameTest extends
PHPUnit_Framework_TestCase
{
  public function testScoreForGutterGameIs0()
  {

    }

}
<?php
require_once 'BowlingGame.php';

class BowlingGameTest extends
PHPUnit_Framework_TestCase
{
  public function testScoreForGutterGameIs0()
  {
     $game = new BowlingGame;
     for ($i = 0; $i < 20; $i++) {
          $game->roll(0);
     }

        $this->assertEquals(0, $game->score());
    }

}
fullo@teletran ~ % phpunit --skeleton-class BowlingGameTest
PHPUnit 3.4.2 by Sebastian Bergmann.
Wrote skeleton for "BowlingGame" to "BowlingGame.php".

<?php
class BowlingGame
{

    public function roll()
    {
       // Remove the following line when you
       // implement this method.

        throw new RuntimeException('Not yet implemented.');
    }

    public function score()
    {
       // Remove the following line when you
       // implement this method.

        throw new RuntimeException('Not yet implemented.');
    }
}
fullo@teletran ~ % phpunit BowlingGameTest
PHPUnit 3.4.2 by Sebastian Bergmann.

E

Time: 0 seconds

There was 1 error:

1) BowlingGameTest::testScoreForGutterGameIs0
RuntimeException: Not yet implemented.
/home/fullo/BowlingGame.php:10
/home/fullo/BowlingGameTest.php:11

FAILURES!
Tests: 1, Assertions: 0, Errors: 1.
<?php
class BowlingGame
{

     public function roll($pins)
     {

     }

     public function score()
     {
        return 0;
     }
}
?>
fullo@teletran ~ % phpunit --colors BowlingGameTest
PHPUnit 3.4.2 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 1 assertion)
We have to write the
tests when the code is
        fresh.
<?php
require_once 'BowlingGame.php';

class BowlingGameTest extends PHPUnit_Framework_TestCase
{
   // …

    public function testScoreForAllOnesIs20()
    {
      $game = new BowlingGame;

        for ($i = 0; $i < 20; $i++) {
            $game->roll(1);
        }

        $this->assertEquals(20, $game->score());
    }
}
fullo@teletran ~ % phpunit –colors
BowlingGameTest
PHPUnit 3.4.2 by Sebastian Bergmann.

.F

Time: 0 seconds

There was 1 failure:

1) BowlingGameTest::testScoreForAllOnesIs20
Failed asserting that <integer:0> matches
expected value <integer:20>.
/home/fullo/BowlingGameTest.php:25

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
<?php
class BowlingGame
{

    protected $rolls = array();

    public function roll($pins)
    {
       $this->rolls[] = $pins;
    }

    public function score()
    {
        return array_sum($this->rolls);
    }
}
<?php
require_once 'BowlingGame.php';

class BowlingGameTest extends PHPUnit_Framework_TestCase
{
   // …

    public function testScoreForOneSpareAnd3Is16()
    {
      $game = new BowlingGame;

        $game->roll(5);
        $game->roll(5);
        $game->roll(3);

        // a function to to roll X times
        $this->rollMany(17, 0);
        $this->assertEquals(16, $game->score());
    }

}
fullo@teletran ~ % phpunit –colors
BowlingGameTest
PHPUnit 3.4.2 by Sebastian Bergmann.

..F

Time: 0 seconds

There was 1 failure:

1) BowlingGameTest::testScoreForOneSpareAnd3is16
Failed asserting that <integer:13> matches
expected value <integer:16>.
/home/fullo/BowlingGameTest.php:33

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
Tests can serve
as an executable
  specification.
fullo@teletran ~ % phpunit --testdox BowlingGameTest
PHPUnit 3.4.2 by Sebastian Bergmann.

BowlingGame
[x] Score for gutter game is 0
[ ] Score for all ones is 20
[ ] Score for one spare and 3 is 16
[ ] Score for one strike and 3 and 4 is 24
[ ] Score for perfect game is 300
Why Unit Test.
To write only clean
 and useful code.
To easily iterate in
development design.
To check for
regressions.
?
For more info see Sebastian
     Bergmann's Bowling Kata
            Workshop!

http://www.slideshare.net/sebastian
  _bergmann/quality-assurance-in-
        php-projects-2164371
Francesco Fullone
     ff AT ideato.it
    skype: ffullone




 via Quinto Bucci 205
  47023 Cesena (FC)
    info AT ideato.it
     www.ideato.it

TDD, a starting point...

  • 1.
    TDD, a startingpoint A PHP perspective... Francesco Fullone, Ideato.it ff AT ideato.it
  • 2.
    Who am I FrancescoFullone aka Fullo - PHP developer since 1999 - President - and Open Source Evangelist - CEO @ - Nerd and geek
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
    We need toconfirm that the code is working after any changes.
  • 9.
  • 11.
  • 12.
    Easy to write Easyto read Isolated Composable
  • 13.
    An example: The bowlingkata (courtesy grabbed from Sebastian Bergmann)
  • 14.
    10 frames 2 rollsto knock down the 10 pins Score for a frame is the number of pins knocked down Bonus for a spare (all 10 pins knocked down in two tries): next roll Bonus for a strike (all 10 pins knocked down in one try): next two rolls Extra rolls for spare or strike in the 10th frame
  • 15.
    <?php require_once 'BowlingGame.php'; class BowlingGameTestextends PHPUnit_Framework_TestCase { } ?>
  • 16.
    <?php require_once 'BowlingGame.php'; class BowlingGameTestextends PHPUnit_Framework_TestCase { public function testScoreForGutterGameIs0() { } }
  • 17.
    <?php require_once 'BowlingGame.php'; class BowlingGameTestextends PHPUnit_Framework_TestCase { public function testScoreForGutterGameIs0() { $game = new BowlingGame; for ($i = 0; $i < 20; $i++) { $game->roll(0); } $this->assertEquals(0, $game->score()); } }
  • 18.
    fullo@teletran ~ %phpunit --skeleton-class BowlingGameTest PHPUnit 3.4.2 by Sebastian Bergmann. Wrote skeleton for "BowlingGame" to "BowlingGame.php". <?php class BowlingGame { public function roll() { // Remove the following line when you // implement this method. throw new RuntimeException('Not yet implemented.'); } public function score() { // Remove the following line when you // implement this method. throw new RuntimeException('Not yet implemented.'); } }
  • 19.
    fullo@teletran ~ %phpunit BowlingGameTest PHPUnit 3.4.2 by Sebastian Bergmann. E Time: 0 seconds There was 1 error: 1) BowlingGameTest::testScoreForGutterGameIs0 RuntimeException: Not yet implemented. /home/fullo/BowlingGame.php:10 /home/fullo/BowlingGameTest.php:11 FAILURES! Tests: 1, Assertions: 0, Errors: 1.
  • 20.
    <?php class BowlingGame { public function roll($pins) { } public function score() { return 0; } } ?>
  • 21.
    fullo@teletran ~ %phpunit --colors BowlingGameTest PHPUnit 3.4.2 by Sebastian Bergmann. . Time: 0 seconds OK (1 test, 1 assertion)
  • 22.
    We have towrite the tests when the code is fresh.
  • 23.
    <?php require_once 'BowlingGame.php'; class BowlingGameTestextends PHPUnit_Framework_TestCase { // … public function testScoreForAllOnesIs20() { $game = new BowlingGame; for ($i = 0; $i < 20; $i++) { $game->roll(1); } $this->assertEquals(20, $game->score()); } }
  • 24.
    fullo@teletran ~ %phpunit –colors BowlingGameTest PHPUnit 3.4.2 by Sebastian Bergmann. .F Time: 0 seconds There was 1 failure: 1) BowlingGameTest::testScoreForAllOnesIs20 Failed asserting that <integer:0> matches expected value <integer:20>. /home/fullo/BowlingGameTest.php:25 FAILURES! Tests: 2, Assertions: 2, Failures: 1.
  • 25.
    <?php class BowlingGame { protected $rolls = array(); public function roll($pins) { $this->rolls[] = $pins; } public function score() { return array_sum($this->rolls); } }
  • 26.
    <?php require_once 'BowlingGame.php'; class BowlingGameTestextends PHPUnit_Framework_TestCase { // … public function testScoreForOneSpareAnd3Is16() { $game = new BowlingGame; $game->roll(5); $game->roll(5); $game->roll(3); // a function to to roll X times $this->rollMany(17, 0); $this->assertEquals(16, $game->score()); } }
  • 27.
    fullo@teletran ~ %phpunit –colors BowlingGameTest PHPUnit 3.4.2 by Sebastian Bergmann. ..F Time: 0 seconds There was 1 failure: 1) BowlingGameTest::testScoreForOneSpareAnd3is16 Failed asserting that <integer:13> matches expected value <integer:16>. /home/fullo/BowlingGameTest.php:33 FAILURES! Tests: 2, Assertions: 2, Failures: 1.
  • 28.
    Tests can serve asan executable specification.
  • 29.
    fullo@teletran ~ %phpunit --testdox BowlingGameTest PHPUnit 3.4.2 by Sebastian Bergmann. BowlingGame [x] Score for gutter game is 0 [ ] Score for all ones is 20 [ ] Score for one spare and 3 is 16 [ ] Score for one strike and 3 and 4 is 24 [ ] Score for perfect game is 300
  • 30.
  • 31.
    To write onlyclean and useful code.
  • 32.
    To easily iteratein development design.
  • 33.
  • 34.
  • 35.
    For more infosee Sebastian Bergmann's Bowling Kata Workshop! http://www.slideshare.net/sebastian _bergmann/quality-assurance-in- php-projects-2164371
  • 36.
    Francesco Fullone ff AT ideato.it skype: ffullone via Quinto Bucci 205 47023 Cesena (FC) info AT ideato.it www.ideato.it