Slideshare.net (beta)

 

All comments

Add a comment on Slide 1

If you have a SlideShare account, login to comment; else you can comment as a guest


Showing 1-50 of 8 (more)

Testing with PHPUnit and Selenium

From sebastian_bergmann, 9 months ago

2860 views  |  0 comments  |  8 favorites  |  5 embeds (Stats)
Download not available ?
 

Groups / Events

 

 
Embed
options

More Info

CC Attribution-NonCommercial-NoDerivs LicenseCC Attribution-NonCommercial-NoDerivs LicenseCC Attribution-NonCommercial-NoDerivs License
This slideshow is Public
Total Views: 2860
on Slideshare: 2601
from embeds: 259

Slideshow transcript

Slide 1: Welcome! Testing with PHPUnit & Selenium Sebastian Bergmann http://sebastian-bergmann.de/ Max Horváth studiVZ Ltd. November 5th 2007

Slide 2: Who are you? Your experience with ● PHP 5? – OOP? – Testing? – PHPUnit? ● Selenium? ● Software Metrics? –

Slide 3: Why test? Companies develop more and more ● enterprise-critical applications with PHP. Tests help to make sure that these ● applications work correctly.

Slide 4: Debugging Sucks, Testing Rocks! Testing Rocks! Debugging Sucks!

Slide 5: What needs testing? Web Application ● Backend – Business Logic ● Reusable Components ● Frontend – Form Processing, Templates, ... ● “Rich Interfaces” with AJAX, JSON, ... ● Feeds, Web Services, ... ●

Slide 6: And how do we test it? Web Application ● Backend – Functional Testing of the business logic with Unit Tests ● Reusable Components ● External components should have their own unit tests. – Frontend – Acceptance Tests or System Tests that are run “in the ● browser” Testing of feeds, web services, etc. with unit tests ● Compatibility Tests for Operating System / Browser ● combinations Performance Tests and Security Tests ●

Slide 7: Testing Software Component Tests ● Executable code fragments, so called Unit Tests, – test the correctness of parts, units, of the software (system under test) System Tests ● Conducted on a complete, integrated system to – evaluate the system's compliance with its specified requirements. (Wikipedia) Non-Functional Tests ● Performance, Stability, Security, ... –

Slide 8: Testing Software Developer Tests ● Ensure that the code works correctly – Acceptance Tests ● Ensure that the code does what the customer – wants

Slide 9: How do you test code? <?php class Calculator { public function add($a, $b) { return $a + $b; } } ?>

Slide 10: How do you test code? <?php $fixture = new Calculator; print $fixture->add(0, 1); $fixture = new Calculator; print $fixture->add(1, 0); ?> 1 1 The tests are not specified in the code. ✗ The tests are not automatically evaluated. ✗

Slide 11: How do you test code? <?php $fixture = new Calculator; print $fixture->add(0, 1) == 1 ? 'pass' : 'fail'; $fixture = new Calculator; print $fixture->add(1, 0) == 1 ? 'pass' : 'fail'; ?> pass pass The tests are specified in the code. ✔ The tests are automatically evaluated. ✔ The writing of tests is tedious. ✗

Slide 12: How do you test code? <?php $fixture = new Calculator; assertTrue($fixture->add(0, 1) == 1); $fixture = new Calculator; assertTrue($fixture->add(1, 0) == 1); function assertTrue($expression) { if (!$expression) { throw new Exception('Assertion failed.'); } } ?> The tests are automatically evaluated. ✔ assertTrue() makes the writing of tests easy. ✔ The test environment is not reusable. ✗

Slide 13: Test Tools To make (code) testing viable, good tool ● support is needed. This is where a testing framework such as ● PHPUnit comes into play. Requirements ● Reusable test environment – Strict separation of production code and test code – Automatic execution of test code – Analysis of the result – Easy to learn to use and easy to use –

Slide 14: Test Tools for PHP / Web Unit Tests ● PHPUnit – SimpleTest – System Tests ● Selenium – PHPUnit + Selenium ● Non-Functional Tests ● Performance, Load, Stress, Availability, ... – ab, httperf, JMeter, Grinder, OpenSTA, ... ● Security – Chorizo ●

Slide 15: PHPUnit: Webseite http://www.phpunit.de/

Slide 16: PHPUnit: Installation

Slide 17: The Bowling Game Kata Introduction: Scoring Bowling The game consists of 10 frames as shown ● below. In each frame the player has two opportunities – to knock down 10 pins. The score for the frame is the total number of – pins knocked down, plus bonuses for strikes and spares.

Slide 18: The Bowling Game Kata Introduction: Scoring Bowling A spare is when the player knocks down all ● 10 pins in two tries. The bonus for that frame is the number of pins – knocked down by the next roll. So in frame 3 below, the score is 10 (the total – number knocked down) plus a bonus of 5 (the number of pins knocked down on the next roll).

Slide 19: The Bowling Game Kata Introduction: Scoring Bowling A strike is when the player knocks down all ● 10 pins on his first try. The bonus for that frame is the value of the next – two balls rolled.

Slide 20: The Bowling Game Kata Introduction: Scoring Bowling In the tenth frame a player who rolls a spare ● or strike is allowed to roll the extra balls to complete the frame. However no more than three balls can be ● rolled in tenth frame.

Slide 21: The Bowling Game Kata Introduction: Requirements Write a class named BowlingGame that has ● two methods roll($pins) is called each time the player rolls – a ball. The argument is the number of pins knocked down. ● score() is called only at the very end of the – game and returns the total score for that game.

Slide 22: The Bowling Game Kata Introduction: Design A game has 10 frames. ● A frame has 1 or 2 rolls. – The 10th frame has 2 or 3 rolls and is different – from all the other frames. The score() method must iterate over all ● the frames and calculate all their scores. The score for a spare or a strike depends on the – frame's successor.

Slide 23: The Bowling Game Kata The first test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { }

Slide 24: The Bowling Game Kata The first test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { public function testGutterGame() { $game = new BowlingGame; } }

Slide 25: The Bowling Game Kata The first test <?php class BowlingGame { }

Slide 26: The Bowling Game Kata The first test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. . Time: 0 seconds OK (1 test)

Slide 27: The Bowling Game Kata The first test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { public function testGutterGame() { $game = new BowlingGame; for ($i = 0; $i < 20; $i++) { $game->roll(0); } } }

Slide 28: The Bowling Game Kata The first test <?php class BowlingGame { public function roll($pins) { } }

Slide 29: The Bowling Game Kata The first test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. . Time: 0 seconds OK (1 test)

Slide 30: The Bowling Game Kata The first test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { public function testGutterGame() { $game = new BowlingGame; for ($i = 0; $i < 20; $i++) { $game->roll(0); } $this->assertEquals(0, $game->score()); } }

Slide 31: The Bowling Game Kata The first test <?php class BowlingGame { public function roll($pins) { } public function score() { return -1; } }

Slide 32: The Bowling Game Kata The first test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. F Time: 0 seconds There was 1 failure: 1) testGutterGame(BowlingGameTest) Failed asserting that <integer:-1> matches expected value <integer:0>. /home/sb/BowlingGameTest.php:14 FAILURES! Tests: 1, Failures: 1.

Slide 33: The Bowling Game Kata The first test <?php class BowlingGame { public function roll($pins) { } public function score() { return 0; } }

Slide 34: The Bowling Game Kata The first test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. . Time: 0 seconds OK (1 test)

Slide 35: Interlude Test-Driven Development Method of designing software, ● not just a method of testing software Test – What do we want X to do? ● How do we want to tell X to do it? ● How will we know when X has done it? ● Code – How does X do it? ● Tests drive the development ● Tests written before code – No code without tests – This slide contains material by Brady Kelly.

Slide 36: The Bowling Game Kata The second test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testAllOnes() { $game = new BowlingGame; for ($i = 0; $i < 20; $i++) { $game->roll(1); } $this->assertEquals(20, $game->score()); } }

Slide 37: The Bowling Game Kata The second test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. .F Time: 0 seconds There was 1 failure: 1) testAllOnes(BowlingGameTest) Failed asserting that <integer:0> matches expected value <integer:20>. /home/sb/BowlingGameTest.php:25 FAILURES! Tests: 2, Failures: 1.

Slide 38: The Bowling Game Kata The second test <?php class BowlingGame { protected $score = 0; public function roll($pins) { $this->score += $pins; } public function score() { return $this->score; } }

Slide 39: The Bowling Game Kata The second test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests)

Slide 40: The Bowling Game Kata The second test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testAllOnes() { $game = new BowlingGame; for ($i = 0; $i < 20; $i++) { $game->roll(1); } $this->assertEquals(20, $game->score()); } }

Slide 41: The Bowling Game Kata The second test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testAllOnes() { $game = new BowlingGame; for ($i = 0; $i < 20; $i++) { $game->roll(1); } $this->assertEquals(20, $game->score()); } }

Slide 42: Interlude Test Fixture One of the most time-consuming parts of ● writing tests is writing the code to set the world up in a known state and then return it to its original state when the test is complete. This known state is called the fixture of the ● test.

Slide 43: Interlude Test Fixture In our BowlingGameTest example the test fixture ● was a single object. Most of the time, though, the fixture will be more ● complex than a simple object, and the amount of code needed to set it up will grow accordingly. The actual content of the test gets lost in the noise ● of setting up the fixture. This problem gets even worse when you write – several tests with similar fixtures. PHPUnit supports sharing the setup code through ● the setUp() and tearDown() template methods.

Slide 44: The Bowling Game Kata The second test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { protected $game; protected function setUp() { $this->game = new BowlingGame; } // ... public function testAllOnes() { for ($i = 0; $i < 20; $i++) { $this->game->roll(1); } $this->assertEquals(20, $this->game->score()); } // ... }

Slide 45: The Bowling Game Kata The second test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { protected $game; protected function setUp() { $this->game = new BowlingGame; } // ... public function testAllOnes() { for ($i = 0; $i < 20; $i++) { $this->game->roll(1); } $this->assertEquals(20, $this->game->score()); } // ... }

Slide 46: The Bowling Game Kata The second test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... protected function rollMany($n, $pins) { for ($i = 0; $i < $n; $i++) { $this->game->roll($pins); } } public function testGutterGame() { $this->rollMany(20, 0); $this->assertEquals(0, $this->game->score()); } public function testAllOnes() { $this->rollMany(20, 1); $this->assertEquals(20, $this->game->score()); } }

Slide 47: The Bowling Game Kata The second test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... protected function rollMany($n, $pins) { for ($i = 0; $i < $n; $i++) { $this->game->roll($pins); } } public function testGutterGame() { $this->rollMany(20, 0); $this->assertEquals(0, $this->game->score()); } public function testAllOnes() { $this->rollMany(20, 1); $this->assertEquals(20, $this->game->score()); } }

Slide 48: Interlude Refactoring A code refactoring is any change to a ● computer program's code which improves its readability or simplifies its structure without changing its results. (Wikipedia) 1.All unit tests run correctly. 2.The code communicates its design principles. 3.The code contains no redundancies. 4.The code contains the minimal number of classes and methods.

Slide 49: The Bowling Game Kata The third test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testOneSpare() { $this->game->roll(5); $this->game->roll(5); // Spare $this->game->roll(3); $this->rollMany(17, 0); $this->assertEquals(16, $this->game->score()); } }

Slide 50: The Bowling Game Kata The third test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. ..F Time: 0 seconds There was 1 failure: 1) testOneSpare(BowlingGameTest) Failed asserting that <integer:13> matches expected value <integer:16>. /home/sb/BowlingGameTest.php:38 FAILURES! Tests: 3, Failures: 1.

Slide 51: The Bowling Game Kata The third test <?php class BowlingGame { protected $score = 0; public function roll($pins) { $this->score += $pins; } public function score() { return $this->score; } }

Slide 52: The Bowling Game Kata The third test <?php class BowlingGame { protected $score = 0; protected $rolls = array(); public function roll($pins) { $this->score += $pins; $this->rolls[] = $pins; } public function score() { return $this->score; } }

Slide 53: The Bowling Game Kata The third test <?php class BowlingGame { protected $rolls = array(); public function roll($pins) { $this->rolls[] = $pins; } public function score() { $score = 0; foreach ($this->rolls as $roll) { $score += $roll; } return $score; } }

Slide 54: The Bowling Game Kata The third test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. ..F Time: 0 seconds There was 1 failure: 1) testOneSpare(BowlingGameTest) Failed asserting that <integer:13> matches expected value <integer:16>. /home/sb/BowlingGameTest.php:38 FAILURES! Tests: 3, Failures: 1.

Slide 55: The Bowling Game Kata The third test <?php class BowlingGame { // ... public function score() { $score = 0; for ($i = 0; $i < count($this->rolls); $i++) { if ($this->rolls[$i] + $this->rolls[$i + 1] == 10) { // ... } $score += $this->rolls[$i]; } return $score; } }

Slide 56: The Bowling Game Kata The third test <?php class BowlingGame { // ... public function score() { $score = 0; $i = 0; for ($frame = 0; $frame < 10; $frame++) { $score += $this->rolls[$i] + $this->rolls[$i + 1]; $i += 2; } return $score; } }

Slide 57: The Bowling Game Kata The third test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. ..F Time: 0 seconds There was 1 failure: 1) testOneSpare(BowlingGameTest) Failed asserting that <integer:13> matches expected value <integer:16>. /home/sb/BowlingGameTest.php:38 FAILURES! Tests: 3, Failures: 1.

Slide 58: The Bowling Game Kata The third test <?php class BowlingGame { // ... public function score() { $score = 0; $i = 0; for ($frame = 0; $frame < 10; $frame++) { // Spare if ($this->rolls[$i] + $this->rolls[$i + 1] == 10) { $score += 10 + $this->rolls[$i + 2]; } else { $score += $this->rolls[$i] + $this->rolls[$i + 1]; } $i += 2; } return $score; } }

Slide 59: The Bowling Game Kata The third test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. ... Time: 0 seconds OK (3 tests)

Slide 60: The Bowling Game Kata The third test <?php class BowlingGame { // ... public function score() { $score = 0; $i = 0; for ($frame = 0; $frame < 10; $frame++) { // Spare if ($this->rolls[$i] + $this->rolls[$i + 1] == 10) { $score += 10 + $this->rolls[$i + 2]; } else { $score += $this->rolls[$i] + $this->rolls[$i + 1]; } $i += 2; } return $score; } }

Slide 61: The Bowling Game Kata The third test <?php class BowlingGame { // ... public function score() { $score = 0; $i = 0; for ($frame = 0; $frame < 10; $frame++) { // Spare if ($this->rolls[$i] + $this->rolls[$i + 1] == 10) { $score += 10 + $this->rolls[$i + 2]; } else { $score += $this->rolls[$i] + $this->rolls[$i + 1]; } $i += 2; } return $score; } }

Slide 62: The Bowling Game Kata The third test <?php class BowlingGame { // ... public function score() { $score = 0; $frameIndex = 0; for ($frame = 0; $frame < 10; $frame++) { // Spare if ($this->rolls[$frameIndex] + $this->rolls[$frameIndex + 1] == 10) { $score += 10 + $this->rolls[$frameIndex + 2]; } else { $score += $this->rolls[$frameIndex] + $this->rolls[$frameIndex + 1]; } $frameIndex += 2; } return $score; } }

Slide 63: The Bowling Game Kata The third test <?php class BowlingGame { // ... public function score() { $score = 0; $frameIndex = 0; for ($frame = 0; $frame < 10; $frame++) { if ($this->isSpare($frameIndex)) { $score += 10 + $this->rolls[$frameIndex + 2]; } else { $score += $this->rolls[$frameIndex] + $this->rolls[$frameIndex + 1]; } $frameIndex += 2; } return $score; } protected function isSpare($frameIndex) { return $this->rolls[$frameIndex] + $this->rolls[$frameIndex + 1] == 10; } }

Slide 64: The Bowling Game Kata The third test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testOneSpare() { $this->game->roll(5); $this->game->roll(5); // Spare $this->game->roll(3); $this->rollMany(17, 0); $this->assertEquals(16, $this->game->score()); } }

Slide 65: The Bowling Game Kata The third test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testOneSpare() { $this->game->roll(5); $this->game->roll(5); // Spare $this->game->roll(3); $this->rollMany(17, 0); $this->assertEquals(16, $this->game->score()); } }

Slide 66: The Bowling Game Kata The third test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... protected function rollSpare() { $this->game->roll(5); $this->game->roll(5); } // ... public function testOneSpare() { $this->rollSpare(); $this->game->roll(3); $this->rollMany(17, 0); $this->assertEquals(16, $this->game->score()); } }

Slide 67: The Bowling Game Kata The fourth test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testOneStrike() { $this->game->roll(10); // Strike $this->game->roll(3); $this->game->roll(4); $this->rollMany(17, 0); $this->assertEquals(24, $this->game->score()); } }

Slide 68: The Bowling Game Kata The fourth test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. ...F Time: 0 seconds There was 1 failure: 1) testOneStrike(BowlingGameTest) Failed asserting that <integer:17> matches expected value <integer:24>. /home/sb/BowlingGameTest.php:52 FAILURES! Tests: 4, Failures: 1.

Slide 69: The Bowling Game Kata The fourth test <?php class BowlingGame { // ... public function score() { $score = 0; $frameIndex = 0; for ($frame = 0; $frame < 10; $frame++) { if ($this->isSpare($frameIndex)) { $score += 10 + $this->rolls[$frameIndex + 2]; } else { $score += $this->rolls[$frameIndex] + $this->rolls[$frameIndex + 1]; } $frameIndex += 2; } return $score; } }

Slide 70: The Bowling Game Kata The fourth test <?php class BowlingGame { // ... public function score() { $score = 0; $frameIndex = 0; for ($frame = 0; $frame < 10; $frame++) { // Strike if ($this->rolls[$frameIndex] == 10) { $score += 10 + $this->rolls[$frameIndex + 1] + $this->rolls[$frameIndex + 2]; $frameIndex++; } // ... } return $score; } }

Slide 71: The Bowling Game Kata The fourth test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. .... Time: 0 seconds OK (4 tests)

Slide 72: The Bowling Game Kata The fourth test <?php class BowlingGame { // ... public function score() { $score = 0; $frameIndex = 0; for ($frame = 0; $frame < 10; $frame++) { // Strike if ($this->rolls[$frameIndex] == 10) { $score += 10 + $this->rolls[$frameIndex + 1] + $this->rolls[$frameIndex + 2]; $frameIndex++; } // ... } return $score; } }

Slide 73: The Bowling Game Kata The fourth test <?php class BowlingGame { // ... public function score() { $score = 0; $frameIndex = 0; for ($frame = 0; $frame < 10; $frame++) { if ($this->isStrike($frameIndex)) { $score += 10 + $this->rolls[$frameIndex + 1] + $this->rolls[$frameIndex + 2]; $frameIndex++; } // ... } return $score; } protected function isStrike($frameIndex) { return $this->rolls[$frameIndex] == 10; } }

Slide 74: The Bowling Game Kata The fourth test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testOneStrike() { $this->game->roll(10); // Strike $this->game->roll(3); $this->game->roll(4); $this->rollMany(17, 0); $this->assertEquals(24, $this->game->score()); } }

Slide 75: The Bowling Game Kata The fourth test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... protected function rollStrike() { $this->game->roll(10); } // ... public function testOneStrike() { $this->rollStrike(); $this->game->roll(3); $this->game->roll(4); $this->rollMany(17, 0); $this->assertEquals(24, $this->game->score()); } }

Slide 76: The Bowling Game Kata The fourth test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. .... Time: 0 seconds OK (4 tests)

Slide 77: The Bowling Game Kata The fifth test <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testPerfectGame() { $this->rollMany(12, 10); $this->assertEquals(300, $this->game->score()); } }

Slide 78: The Bowling Game Kata The fifth test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. ..... Time: 0 seconds OK (5 tests)

Slide 79: The Bowling Game Kata Final version of the BowlingGame class <?php class BowlingGame { protected $rolls = array(); public function roll($pins) { $this->rolls[] = $pins; } // ... }

Slide 80: The Bowling Game Kata Final version of the BowlingGame class <?php class BowlingGame { // ... protected function isSpare($frameIndex) { return $this->sumOfPinsInFrame($frameIndex) == 10; } protected function isStrike($frameIndex) { return $this->rolls[$frameIndex] == 10; } protected function sumOfPinsInFrame($frameIndex) { return $this->rolls[$frameIndex] + $this->rolls[$frameIndex + 1]; } }

Slide 81: The Bowling Game Kata Final version of the BowlingGame class <?php class BowlingGame { // ... protected function spareBonus($frameIndex) { return $this->rolls[$frameIndex + 2]; } protected function strikeBonus($frameIndex) { return $this->rolls[$frameIndex + 1] + $this->rolls[$frameIndex + 2]; } }

Slide 82: The Bowling Game Kata Final version of the BowlingGame class <?php class BowlingGame { // ... public function score() { $score = 0; $frameIndex = 0; for ($frame = 0; $frame < 10; $frame++) { if ($this->isStrike($frameIndex)) { $score += 10 + $this->strikeBonus($frameIndex); $frameIndex++; } else if ($this->isSpare($frameIndex)) { $score += 10 + $this->spareBonus($frameIndex); $frameIndex += 2; } else { $score += $this->sumOfPinsInFrame($frameIndex); $frameIndex += 2; } } return $score; } }

Slide 83: The Bowling Game Kata The fifth test sb@vmware ~ % phpunit BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. ..... Time: 0 seconds OK (5 tests)

Slide 84: PHPUnit Agile Documentation <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testGutterGame() { // ... } public function testAllOnes() { // ... } public function testOneSpare() { // ... } public function testOneStrike() { // ... } public function testPerfectGame() { // ... } }

Slide 85: PHPUnit Agile Documentation <?php require_once 'BowlingGame.php'; class BowlingGameTest extends PHPUnit_Framework_TestCase { // ... public function testScoreForGutterGameIs0() { // ... } public function testScoreForAllOnesIs20() { // ... } public function testScoreForOneSpareIs16() { // ... } public function testScoreForOneStrikeIs24() { // ... } public function testScoreForPerfectGameIs300() { // ... } }

Slide 86: PHPUnit Agile Documentation sb@vmware ~ % phpunit --testdox BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. BowlingGame - Score for gutter game is 0 - Score for all ones is 20 - Score for one spare is 16 - Score for one strike is 24 - Score for perfect game is 300

Slide 87: PHPUnit Code Coverage sb@vmware ~ % phpunit --report report BowlingGameTest PHPUnit 3.2.0 by Sebastian Bergmann. ..... Time: 0 seconds OK (5 tests) Generating report, this may take a moment.

Slide 88: Organizing Test Suites Application/ Package/ – Class ● (Application/Package/Class.php) ... ● ... – Tests/ – AllTests.php ● Package/ ● AllTests.php – ClassTest – (Application/Tests/Package/ClassTest.php)

Slide 89: Organizing Test Suites <?php require_once 'PHPUnit/Framework.php'; require_once 'Application/Tests/Package/AllTests.php'; class AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Project'); $suite->addTest(Package_AllTests::suite()); return $suite; } } ?>

Slide 90: Organizing Test Suites <?php require_once 'PHPUnit/Framework.php'; require_once 'Application/Tests/Package/ClassTest.php'; class Package_AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Package'); $suite->addTestSuite('Package_ClassTest'); return $suite; } } ?>

Slide 91: Annotations @test <?php class Specification extends PHPUnit_Framework_TestCase { /** * @test */ public function shouldDoSomething() { } /** * @test */ public function shouldDoSomethingElse() { } }

Slide 92: Annotations @test sb@vmware ~ % phpunit --testdox Specification PHPUnit 3.2.0 by Sebastian Bergmann. Specification - Should do something - Should do something else

Slide 93: Annotations @assert <?php class Calculator { /** * @assert (1, 2) == 3 */ public function add($a, $b) { return $a + $b; } /** * @assert (2, 1) == 1 */ public function sub($a, $b) { return $a - $b; } }

Slide 94: Annotations @assert sb@vmware ~ % phpunit Calculator PHPUnit 3.2.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests)

Slide 95: Annotations @group <?php class SomeTest extends PHPUnit_Framework_TestCase { /** * @group specification */ public function testSomething() { } /** * @group regresssion * @group bug2204 */ public function testSomethingElse() { } }

Slide 96: Annotations @group sb@vmware ~ % phpunit SomeTest PHPUnit 3.2.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests) sb@vmware ~ % phpunit --group bug2204 SomeTest PHPUnit 3.2.0 by Sebastian Bergmann. . Time: 0 seconds OK (1 test)

Slide 97: Annotations @dataProvider <?php class DataTest extends PHPUnit_Framework_TestCase { /** * @dataProvider provider */ public function testAdd($a, $b, $c) { $this->assertEquals($c, $a + $b); } public static function provider() { return array( array(0, 0, 0), array(0, 1, 1), array(1, 0, 1), array(1, 1, 3) ); } }

Slide 98: Annotations @dataProvider sb@vmware ~ % phpunit DataTest PHPUnit 3.2.0 by Sebastian Bergmann. ...F Time: 0 seconds There was 1 failure: 1) testAdd(DataTest) with data (1, 1, 3) Failed asserting that <integer:2> matches expected value <integer:3>. /home/sb/DataTest.php:9 FAILURES! Tests: 4, Failures: 1.

Slide 99: Logging XML <?xml version=\"1.0\" encoding=\"UTF-8\"?> <testsuites> <testsuite name=\"CalculatorTest\" file=\"/home/sb/CalculatorTest.php\" tests=\"5\" failures=\"1\" errors=\"0\" time=\"0.0021438598632812\"> <testcase name=\"testAdd\" class=\"CalculatorTest\" file=\"/home/sb/CalculatorTest.php\" time=\"0.00045299530029297\"/> <testcase name=\"testAdd2\" class=\"CalculatorTest\" file=\"/home/sb/CalculatorTest.php\" time=\"0.0003299713134765\"/> <testcase name=\"testAdd3\" class=\"CalculatorTest\" file=\"/home/sb/CalculatorTest.php\" time=\"0.0003290176391601\"/> <testcase name=\"testAdd4\" class=\"CalculatorTest\" file=\"/home/sb/CalculatorTest.php\" time=\"0.0003290176391601\"/> <testcase name=\"testAdd5\" class=\"CalculatorTest\" file=\"/home/sb/CalculatorTest.php\" time=\"0.00070285797119141\"> <failure type=\"PHPUnit_Framework_ExpectationFailedException\"> Failed asserting that &lt;integer:3&gt; is not equal to &lt;integer:3&gt;. /home/sb/CalculatorTest.php:39 </failure> </testcase> </testsuite> </testsuites>

Slide 100: Logging JavaScript Object Notation (JSON) {\"event\":\"suiteStart\",\"suite\":\"CalculatorTest\",\"tests\":5} {\"event\":\"test\",\"suite\":\"CalculatorTest\",\"test\":\"testAdd(CalculatorTest)\", \"status\":\"pass\",\"time\":0.0004529953,\"trace\":[],\"message\":\"\"} {\"event\":\"test\",\"suite\":\"CalculatorTest\",\"test\":\"testAdd2(CalculatorTest)\", \"status\":\"pass\",\"time\":0.000329971313,\"trace\":[],\"message\":\"\"} {\"event\":\"test\",\"suite\":\"CalculatorTest\",\"test\":\"testAdd3(CalculatorTest)\", \"status\":\"pass\",\"time\":0.000329017639,\"trace\":[],\"message\":\"\"} {\"event\":\"test\",\"suite\":\"CalculatorTest\",\"test\":\"testAdd4(CalculatorTest)\", \"status\":\"pass\",\"time\":0.000329017639,\"trace\":[],\"message\":\"\"} {\"event\":\"test\",\"suite\":\"CalculatorTest\",\"test\":\"testAdd5(CalculatorTest)\", \"status\":\"fail\",\"time\":0.000702857971,\"trace\":[],\"message\":\"Failed asserting that <integer:3> is not equal to <integer:3>.\"}

Slide 101: Logging Test Anything Protocol (TAP) # TestSuite \"CalculatorTest\" started. ok 1 - testAdd(CalculatorTest) ok 2 - testAdd2(CalculatorTest) ok 3 - testAdd3(CalculatorTest) ok 4 - testAdd4(CalculatorTest) not ok 5 - Failure: testAdd5(CalculatorTest) # TestSuite \"CalculatorTest\" ended. 1..5

Slide 102: Logging GraphViz

Slide 103: PHPUnit Test Framework ● Member of the xUnit family of test frameworks – Mock Objects – Integration ● Selenium RC – Bamboo, Bitten, CruiseControl, ... – Even More Cool Stuff :-) ● Code Coverage and Software Metrics – Test Database – Mutation Testing (GSoC07 project) –

Slide 104: Software Metrics A software metric is a measure of some ● property of a piece of software or its specifications Lines of Code – Cyclomatic Complexity – NPath Complexity – Change Risk Analysis and Predictions (CRAP) – ... – Violations of rules that are based on ● software metrics

Slide 105: Software Metrics Abbreviated list of supported metrics Class Level ● Class Size (CSZ) – Class Interface Size (CIS) – Attribute Inheritance Factor (AIF), Attribute – Hiding Factor (AHF) Method Inheritance Factor (MIF), Method Hiding – Factor (MHF) Polymorphism Factor (PF) – Afferent Coupling (Ce), Efferent Coupling (Ce) – Instability (I = Ce / (Ce+Ca)) ● Depth of Inheritance Tree (DIT) –

Slide 106: Software Metrics Abbreviated list of supported metrics Class Level ● Number of Children (NOC) – Number of Interfaces Implemented (IMPL) – Number of Variables (VARS) – Number of Non-Private Variables (VARSnp) – Number of Variables (VARSi) – Weighted Methods per Class (WMC) – Weighted Non-Private Methods per Class – (WMCnp) Weighted Inherited Methods per Class (WMCi) –

Slide 107: Software Metrics Abbreviated list of supported metrics Project Level ● Number of Interfaces (INTERFS) – Number of Abstract Classes (CLSa) – Number of Concrete Classes (CLSc) – Number of Classes (CLS) – Number of Root Classes (ROOTS) – Number of Leaf Classes (LEAFS) – Maximum Depth of Intheritance Tree (maxDIT) –

Slide 108: Selenium Selenium ● Test web applications in a web browser – Browser Compatibility Testing ● System Functional Testing ● Runs in the browser – Selenium IDE ● IDE for Selenium tests – Extension for Firefox ● Record, execute, edit, debug tests in the browser ●

Slide 109: Selenium Selenium RC Selenium RC ● Automated execution of Selenium tests – Tests can be specified in any language – PHP Bindings: PEAR Testing_Selenium ● PHPUnit natively speaks the Selenium RC protocol ● One test can be executed on multiple – OS / Browser combinations

Slide 110: Selenium Selenium RC

Slide 111: Test-Level Setup <?php class MyTest extends PHPUnit_Framework_TestCase { protected function setUp() { print \"\\nMyTest::setUp()\"; MyTest::setUp() } MyTest::testOne() protected function tearDown() MyTest::tearDown() { print \"\\nMyTest::tearDown()\"; MyTest::setUp() } MyTest::testTwo() public function testOne() MyTest::tearDown() { print \"\\nMyTest::testOne()\"; } public function testTwo() { print \"\\nMyTest::testTwo()\"; } }

Slide 112: Suite-Level Setup <?php require_once 'MyTest.php'; class MySuite extends PHPUnit_Framework_TestSuite { MySuite::setUp() public static function suite() { return new MySuite('MyTest'); MyTest::setUp() } MyTest::testOne() MyTest::tearDown() protected function setUp() { MyTest::setUp() print \"\\nMySuite::setUp()\"; } MyTest::testTwo() MyTest::tearDown() protected function tearDown() { print \"\\nMySuite::tearDown()\"; MySuite::tearDown() } }

Slide 113: Suite-Level Setup Setting up a shared fixture <?php require_once 'MyTest.php'; class MySuite extends PHPUnit_Framework_TestSuite { public static function suite() { return new MySuite('MyTest'); } protected function setUp() { $this->sharedFixture = 'something'; } }

Slide 114: Suite-Level Setup Using a shared fixture <?php class MyTest extends PHPUnit_Framework_TestCase { public function testOne() { $this->assertEquals( 'something', $this->sharedFixture ); } }

Slide 115: Testing Object Interaction Stubs Tests that only test one thing are more informative ● than tests where failure can come from many sources How can you isolate your tests from external ● influences? Simply put, by replacing the expensive, messy, ● unreliable, slow, complicated resources with stubs that are automatically generated for the purpose of your tests For example, you can replace what is in reality a ● complicated computation with a constant, at least for the purposes of a single test

Slide 116: Testing Object Interaction Terminology Dummy ● Not the real object – Fake ● Usable for testing but not for – real job Stub ● Fake that returns canned data – Spy ● Stub that records called – methods, etc. Mock ● Spy with expectations –

Slide 117: Testing Object Interaction Stubs <?php require_once 'PHPUnit/Framework.php'; class StubTest extends PHPUnit_Framework_TestCase { public function testStub() { } } ?>

Slide 118: Testing Object Interaction Stubs <?php require_once 'PHPUnit/Framework.php'; class StubTest extends PHPUnit_Framework_TestCase { public function testStub() { $stub = $this->getMock('SomeClass'); } } ?>

Slide 119: Testing Object Interaction Stubs <?php require_once 'PHPUnit/Framework.php'; class StubTest extends PHPUnit_Framework_TestCase { public function testStub() { $stub = $this->getMock('SomeClass'); $stub->expects($this->any()) ->method('doSomething') ->will($this->returnValue('foo')); } } ?>

Slide 120: Testing Object Interaction Stubs <?php require_once 'PHPUnit/Framework.php'; class StubTest extends PHPUnit_Framework_TestCase { public function testStub() { $stub = $this->getMock('SomeClass'); $stub->expects($this->any()) ->method('doSomething') ->will($this->returnValue('foo')); // Calling $stub->doSomething() will now return // 'foo'. } } ?>

Slide 121: Testing Object Interaction Mock Objects Mock Objects are simulated objects that ● mimic the behavior of real objects in controlled ways We can create a mock object to test the ● behavior of some other object The Mock Objects system that is integrated ● in PHPUnit provides a fluent interface to specify the behavior of and the expectations for the mock

Slide 122: Testing Object Interaction Mock Objects <?php require_once 'PHPUnit/Framework.php'; class ObserverTest extends PHPUnit_Framework_TestCase { public function testUpdateIsCalledOnce() { } } ?>

Slide 123: Testing Object Interaction Mock Objects <?php require_once 'PHPUnit/Framework.php'; class ObserverTest extends PHPUnit_Framework_TestCase { public function testUpdateIsCalledOnce() { $observer = $this->getMock( 'Observer', array('update') ); } } ?>

Slide 124: Testing Object Interaction Mock Objects <?php require_once 'PHPUnit/Framework.php'; class ObserverTest extends PHPUnit_Framework_TestCase { public function testUpdateIsCalledOnce() { $observer = $this->getMock( 'Observer', array('update') ); $observer->expects($this->once()) ->method('update') ->with($this->equalTo('something')); } } ?>

Slide 125: Testing Object Interaction Mock Objects <?php require_once 'PHPUnit/Framework.php'; class ObserverTest extends PHPUnit_Framework_TestCase { public function testUpdateIsCalledOnce() { $observer = $this->getMock( 'Observer', array('update') ); $observer->expects($this->once()) ->method('update') ->with($this->equalTo('something')); $subject = new Subject; $subject->attach($observer); $subject->doSomething(); } } ?>

Slide 126: Database Testing Michael Lively Jr. has ported the DbUnit ● extension for JUnit to PHPUnit PHPUnit_Extensions_Database_TestCase ● is used to test database-driven projects and – puts your database into a known state between – test runs This avoids problems with one test corrupting the ● database for other tests has the ability to export and import your – database data to and from XML datasets

Slide 127: Database Testing The BankAccountDB example <?php require_once 'PHPUnit/Extensions/Database/TestCase.php'; class BankAccountDBTest extends PHPUnit_Extensions_Database_TestCase { }

Slide 128: Database Testing The BankAccountDB example <?php require_once 'PHPUnit/Extensions/Database/TestCase.php'; class BankAccountDBTest extends PHPUnit_Extensions_Database_TestCase { protected $pdo; public function __construct() { $this->pdo = new PDO('sqlite::memory:'); BankAccount::createTable($this->pdo); } }

Slide 129: Database Testing The BankAccountDB example <?php require_once 'PHPUnit/Extensions/Database/TestCase.php'; class BankAccountDBTest extends PHPUnit_Extensions_Database_TestCase { protected $pdo; public function __construct() { $this->pdo = new PDO('sqlite::memory:'); BankAccount::createTable($this->pdo); } protected function getConnection() { return $this->createDefaultDBConnection($this->pdo, 'sqlite'); } }

Slide 130: Database Testing The BankAccountDB example <?php require_once 'PHPUnit/Extensions/Database/TestCase.php'; class BankAccountDBTest extends PHPUnit_Extensions_Database_TestCase { protected $pdo; public function __construct() { $this->pdo = new PDO('sqlite::memory:'); BankAccount::createTable($this->pdo); } protected function getConnection() { return $this->createDefaultDBConnection($this->pdo, 'sqlite'); } protected function getDataSet() { return $this->createFlatXMLDataSet('/path/to/seed.xml'); } }

Slide 131: Database Testing The BankAccountDB example <dataset> <account account_number=\"15934903649620486\" balance=\"100.00\" /> <account account_number=\"15936487230215067\" balance=\"1216.00\" /> <account account_number=\"12348612357236185\" balance=\"89.00\" /> <account account_number=\"15936487230215067\" balance=\"1216.00\" /> </dataset>

Slide 132: Database Testing The BankAccountDB example <?php require_once 'PHPUnit/Extensions/Database/TestCase.php'; class BankAccountDBTest extends PHPUnit_Extensions_Database_TestCase { // ... public function testNewAccount() { } }

Slide 133: Database Testing The BankAccountDB example <?php require_once 'PHPUnit/Extensions/Database/TestCase.php'; class BankAccountDBTest extends PHPUnit_Extensions_Database_TestCase { // ... public function testNewAccount() { $ba = new BankAccount('12345678912345678', $this->pdo); } }

Slide 134: Database Testing The BankAccountDB example <?php require_once 'PHPUnit/Extensions/Database/TestCase.php'; class BankAccountDBTest extends PHPUnit_Extensions_Database_TestCase { // ... public function testNewAccount() { $ba = new BankAccount('12345678912345678', $this->pdo); $set = $this->createFlatXMLDataSet( '/path/to/after-new-account.xml' ); } }

Slide 135: Database Testing The BankAccountDB example <?php require_once 'PHPUnit/Extensions/Database/TestCase.php'; class BankAccountDBTest extends PHPUnit_Extensions_Database_TestCase { // ... public function testNewAccount() { $ba = new BankAccount('12345678912345678', $this->pdo); $set = $this->createFlatXMLDataSet( '/path/to/after-new-account.xml' ); $this->assertTablesEqual( $set->getTable('account'), $this->getConnection() ->createDataSet() ->getTable('account') ); } }

Slide 136: Database Testing The BankAccountDB example <dataset> <account account_number=\"15934903649620486\" balance=\"100.00\" /> <account account_number=\"15936487230215067\" balance=\"1216.00\" /> <account account_number=\"12348612357236185\" balance=\"89.00\" /> <account account_number=\"15936487230215067\" balance=\"1216.00\" /> <account account_number=\"12345678912345678\" balance=\"0.00\" /> </dataset>