Unit Testing: First Steps
Upcoming SlideShare
Loading in...5
×
 

Unit Testing: First Steps

on

  • 3,565 views

 

Statistics

Views

Total Views
3,565
Views on SlideShare
3,554
Embed Views
11

Actions

Likes
1
Downloads
37
Comments
0

3 Embeds 11

http://www.slideshare.net 7
http://www.linkedin.com 3
https://www.linkedin.com 1

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

CC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Unit Testing: First Steps Unit Testing: First Steps Presentation Transcript

  • Unit Testing First Steps Jakob Heuser http://www.slideshare.net/jakobo
  • Who? • 10 years in the industry • Academic Skills Achievement Program • Homeland Security Digital Library • Gaia Online • LinkedIn
  • Who?
  • Who?
  • Who?
  • Who?
  • Our Goal • Break down testing myths • Grasp testing (the concept) • See a few tests • Walk out wanting to write some tests
  • Covering • Intro • Simple Tests • Frameworks • Darts • Advocacy This is some code This is a passing test This is a failing test
  • Unit Testing Is • Easier than you think • Probably harder than it should be • An idea not a software package • (though there is software for it)
  • Unit • Smallest testable part of an application
  • Unit • Smallest testable part of an application • Function (or)
  • Unit • Smallest testable part of an application • Function (or) • Public method (or)
  • Unit • Smallest testable part of an application • Function (or) • Public method (or) • Included file
  • Unit Testing Concept • Given X and Y, Z should happen • Make changes to your code • Given X and Y, Z should still happen
  • Testing Requirements not the Code
  • Testing Requirements? • Requirements define code • Serve as documentation for the code alongside the code itself
  • A Very Basic Test <?PHP function addLineBreaks($in_string) { $in_string = str_replace("n", "n<br/>"); return $in_string; }
  • A Very Basic Test <?PHP function addLineBreaks($in_string) { $in_string = str_replace("n", "n<br/>"); return $in_string; } <?PHP $str = "This isna test"; echo "nn===n"; echo addLineBreaks($str); echo "n===nn";
  • Visual Inspection • This is a “test” • Quick to write and verify
  • I Can’t Do That For EVERYTHING!
  • What If... • we had hundreds of tests • we didn’t have time to visually inspect output • we could describe what we want programatically
  • Solutions?
  • Testing Frameworks • Condense our “testing” process • Tell us what went wrong, and why • Automated
  • Testing Frameworks • SnapTest (snaptest.net) • PHPUnit (phpunit.de) • SimpleTest (simpletest.org) • Testilence (testilence.org) • (some MVC frameworks have testing)
  • 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;
  • Refactoring // ... protected function scoreTrio($throw_one, $throw_two, $throw_three) { $score = $throw_one + $throw_two + $throw_three; if ($this->score - $score < 0) { return $this->score; } $this->score = $this->score - $score; 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
  • Appendix http://www.flickr.com/photos/ratterrell/125200561 http://www.flickr.com/photos/wili/214316968 http://www.flickr.com/photos/mommamia/210433527 http://www.flickr.com/photos/syldavia/206163304 http://www.flickr.com/photos/mr_o/3606222762/
  • Appendix http://www.flickr.com/photos/frield/95509221/ http://flickr.com/photos/deerwooduk/579761138 http://www.flickr.com/photos/brom/1329688851