Testing Frameworks
• Every framework has different goals
• Find what’s right for you and your project
SnapTest (5)
SnapTest
• Simplify the testing process
• Lower barrier to entry
(at cost of doing it “all”)
• Run in any environment including via web
PHPUnit (4, 5)
PHPUnit
• Can test pretty much anything
• Most established project
• Test coverage analysis with XDebug
SimpleTest (4, 5)
SimpleTest
• Web test runner support
• Very small footprint
• Simulate browser events
Testilence (5)
Testilence
• PHP 5 targeted
• C shell runner “tence”
So Which One?
• Examples tonight use SnapTest
• Focus on the theory
The Game of Darts
The Rules of Darts
• Start at 501 points
• Goal is to reach 0 points
• 3 throws results in a score
• Must finish on 0 exactly
Our Code
<?PHP
class DartsGame { Score starts
protected $score;
at 501
public function __construct() {
$this->score = 501;
}
public function throwDart($score) {
if ($this->score - $score < 0) {
return $this->score;
}
$this->score = $this->score - $score;
return $this->score;
}
public function getScore() {
return $this->score;
}
}
Our Code
<?PHP
class DartsGame {
protected $score;
public function __construct() {
$this->score = 501;
}
Throw a
public function throwDart($score) {
if ($this->score - $score < 0) { dart
return $this->score;
}
$this->score = $this->score - $score;
return $this->score;
}
public function getScore() {
return $this->score;
}
}
Our Code
<?PHP
class DartsGame {
protected $score;
public function __construct() {
$this->score = 501;
}
public function throwDart($score) {
if ($this->score - $score < 0) {
return $this->score;
}
$this->score = $this->score - $score; Save new
}
return $this->score;
score
public function getScore() {
return $this->score;
}
}
Our Code
<?PHP
class DartsGame {
protected $score;
public function __construct() {
$this->score = 501;
}
public function throwDart($score) {
if ($this->score - $score < 0) {
return $this->score;
}
$this->score = $this->score - $score;
return $this->score;
}
public function getScore() {
}
return $this->score; Get the
} current score
Our First Test
• A new game
• Score should be 501
Our First Test
<?PHP
require_once 'basedarts.php';
class New_Game_No_Throws extends Snap_UnitTestCase {
public function setUp() {
$this->Darts = new DartsGame();
}
public function tearDown() {
unset($this->Darts);
}
public function testScoreIsFiveHundredOne() {
return $this->assertEqual($this->Darts->getScore(), 501);
}
}
Our Results
jakobo@Hikaru ~/Dropbox/2009-06-18 unittesting/resources> snaptest.sh ./
.
______________________________________________________________________
Total Cases: 1
Total Tests: 1
Total Pass: 1
Total Defects: 0
Total Failures: 0
Total Skips: 0
Total Todo: 0
Our Second Test
• A new game
• Throw 10, 0, 0
• Score should be 491
Our Test
<?PHP
require_once 'basedarts.php';
class Throw_Ten_Zero_Zero_Test extends Snap_UnitTestCase { Throw some
public function setUp() { darts
$this->Darts = new DartsGame();
$this->Darts->throwDart(10);
$this->Darts->throwDart(0);
$this->Darts->throwDart(0);
}
public function tearDown() {
unset($this->Darts);
}
public function testScoreIsFourNinteyOne() {
return $this->assertEqual($this->Darts->getScore(), 491);
}
}
Our Test
<?PHP
require_once 'basedarts.php';
class Throw_Ten_Zero_Zero_Test extends Snap_UnitTestCase {
public function setUp() {
$this->Darts = new DartsGame();
$this->Darts->throwDart(10);
$this->Darts->throwDart(0);
$this->Darts->throwDart(0);
Reset dart game
} when done
public function tearDown() {
unset($this->Darts);
}
public function testScoreIsFourNinteyOne() {
return $this->assertEqual($this->Darts->getScore(), 491);
}
}
Our Test
<?PHP
require_once 'basedarts.php';
class Throw_Ten_Zero_Zero_Test extends Snap_UnitTestCase {
public function setUp() {
$this->Darts = new DartsGame();
$this->Darts->throwDart(10);
$this->Darts->throwDart(0);
$this->Darts->throwDart(0);
}
public function tearDown() {
unset($this->Darts);
}
The Test
public function testScoreIsFourNinteyOne() {
return $this->assertEqual($this->Darts->getScore(), 491);
}
}
Our Results
jakobo@Hikaru ~/Dropbox/2009-06-18 unittesting/resources> snaptest.sh ./
..
______________________________________________________________________
Total Cases: 2
Total Tests: 2
Total Pass: 2
Total Defects: 0
Total Failures: 0
Total Skips: 0
Total Todo: 0
Okay, is this working?
Sanity Check
<?PHP
class DartsGame {
protected $score;
public function __construct() {
$this->score = 501;
} Broken in a very
public function throwDart($score) { obvious way
if ($this->score - $score < 0) {
return $this->score;
}
$this->score = 0;
return $this->score;
}
public function getScore() {
return $this->score;
}
}
Predictable Failure
jakobo@Hikaru ~/Dropbox/2009-06-18 unittesting/resources> snaptest.sh ./
.F
Equal (==) assertion failed. [int(491) != int(0)]
in method: testScoreIsFourNinteyOne
in class: Throw_Ten_Zero_Zero_Test
in file: /Users/jakobo/Dropbox/2009-06-18 unittesting/resources/
basedarts.stest.php
______________________________________________________________________
Total Cases: 2
Total Tests: 2
Total Pass: 1
Total Defects: 0
Total Failures: 1
Total Skips: 0
Total Todo: 0
Back to our Rules
• Start at 501 points
• Goal is to reach 0 points
• 3 throws results in a score
• Must finish on 0 exactly
The Next Test
<?PHP
require_once 'basedarts.php';
class Throw_Last_Set_Over_501_Test extends Snap_UnitTestCase {
public function setUp() {
$this->Darts = new DartsGame();
$this->Darts->throwDart(50);
// down by 450 (51 left, next block shouldn’t score)
$this->Darts->throwDart(50);
$this->Darts->throwDart(50);
$this->Darts->throwDart(1);
}
// ...
public function testScoreIsFiftyOne() {
return $this->assertEqual($this->Darts->getScore(), 51);
}
}
Huh...
jakobo@Hikaru ~/Dropbox/2009-06-18 unittesting/resources> snaptest.sh ./
.F.
Equal (==) assertion failed. [int(51) != int(0)]
in method: testScoreIsFiftyOne
in class: Throw_Last_Set_Over_501_Test
in file: /Users/jakobo/Dropbox/2009-06-18 unittesting/resources/
basedarts_overage.stest.php
______________________________________________________________________
Total Cases: 3
Total Tests: 3
Total Pass: 2
Total Defects: 0
Total Failures: 1
Total Skips: 0
Total Todo: 0
When Code Doesn’t Match Requirements
A Bug Found
<?PHP
class DartsGame {
protected $score;
public function __construct() {
$this->score = 501;
}
public function throwDart($score) {
if ($this->score - $score < 0) {
return $this->score; “3 throws results
}
in a score”
$this->score = $this->score - $score;
return $this->score;
}
public function getScore() {
return $this->score;
}
}
Solving the Bug
• Keep track of “turns” and only score every
3 throws
Dealing With the Bug
<?PHP
class DartsGame {
protected $score;
protected $throws;
public function __construct() {
$this->score = 501;
$this->throws = array();
}
public function throwDart($score) { Address the Bug
$this->throws[] = $score;
if (count($this->throws) < 3) {
return $this->score;
}
$score = $this->throws[0] + $this->throws[1] + $this->throws[2];
$this->throws = array();
// ...
}
Fixed!
jakobo@Hikaru ~/Dropbox/2009-06-18 unittesting/resources> snaptest.sh ./
...
______________________________________________________________________
Total Cases: 3
Total Tests: 3
Total Pass: 3
Total Defects: 0
Total Failures: 0
Total Skips: 0
Total Todo: 0
“What If” Thinking
(Rules say: I was supposed to throw in 3’s)
If I don’t throwDart() in a multiple of 3,
what happens?
“What If” Thinking
• “Well then, I’d expect it to throw an
exception”
• Update the requirements, then add a test
“TDD” or “Test First”
<?PHP
require_once 'basedarts_throws.php';
class Throw_Non_Multiple_Of_Three_Test extends Snap_UnitTestCase {
public function setUp() {
$this->Darts = new DartsGame();
$this->Darts->throwDart(50);
$this->Darts->throwDart(50);
}
public function tearDown() {
unset($this->Darts);
} “I expect _____
to happen”
public function testGetScoreThrowsException() {
$this->willThrow("Exception");
$this->Darts->getScore();
$this->assertFalse(true, 'An exception was not thrown');
}
}
Of Course it Fails
jakobo@Hikaru ~/Dropbox/2009-06-18 unittesting/resources> snaptest.sh ./
...F
FALSE assertion got TRUE. [bool(true) !== bool(false)] with user message: An
exception was not thrown
in method: testGetScoreThrowsException
in class: Throw_Non_Multiple_Of_Three_Test
in file: /Users/jakobo/Dropbox/2009-06-18 unittesting/resources/
basedarts_multiple.stest.php
______________________________________________________________________
Total Cases: 4
Total Tests: 4
Total Pass: 3
Total Defects: 0
Total Failures: 1
Total Skips: 0
Total Todo: 0
We should write that part...
Write in Solution
<?PHP
class DartsGame {
// ...
public function getScore() {
if (count($this->throws)) {
throw new Exception('Darts must be thrown in multiples of 3');
}
return $this->score;
}
}
Meeting our
requirement
Our Results
jakobo@Hikaru ~/Dropbox/2009-06-18 unittesting/resources> snaptest.sh ./
....
______________________________________________________________________
Total Cases: 4
Total Tests: 4
Total Pass: 4
Total Defects: 0
Total Failures: 0
Total Skips: 0
Total Todo: 0
You Just Learned “TDD”
Refactoring
• Change the internals freely
• “throws” array
• way score is tallied
• Verify your “rules” are still adhered to
Refactoring
<?PHP
class DartsGame {
// ...
public function throwDart($score) {
$this->throws[] = $score;
if (count($this->throws) < 3) {
return $this->score;
}
$throws = $this->throws;
$this->throws = array();
return $this->scoreTrio($throws[0], $throws[1], $throws[2]);
}
// ...
protected function scoreTrio($throw_one, $throw_two, $throw_three) {
$score = $throw_one + $throw_two + $throw_three;
if ($this->score - $score < 0) {
return $this->score;
And Rerun Tests
jakobo@Hikaru ~/Dropbox/2009-06-18 unittesting/resources> snaptest.sh ./
....
______________________________________________________________________
Total Cases: 4
Total Tests: 4
Total Pass: 4
Total Defects: 0
Total Failures: 0
Total Skips: 0
Total Todo: 0
Advocacy / Recap
• Testing is a good thing
• Testing is easy
• We’re testing requirements, not code
Advocacy / Recap
• There are frameworks for testing
• Tests can be automated
Advocacy / Recap
• You can test new and established code
• You can refactor code safely
Next Steps
• Download a framework
• Check your requirements
• Write some tests
• Be happier!
Unit Testing
First Steps
Jakob Heuser http://www.slideshare.net/jakobo
0 comments
Post a comment