9. Direct descendant of sUnitwritten by Kent Beck and jUnit, written by Beck and Erich GammaWebsite: http://www.phpunit.de Code coverageisprovidedusing the xDebug extension: http://www.xdebug.org
10. Your first PHPUnittest (1) Goal: test an implementation of ROT13 System under test: functionrot13($text) { $len = strlen($text); for ($i = 0; $i < $len; $i++) { $text{$i} = chr((ord($text{$i}) - ord('a') + 13) % 26 + ord('a')); } return$text; } Initial naïve approach: Formulateexpectedbehavior
11. Your first PHPUnittest (2) Class namehas Test suffix Base class for tests class Rot13Test extendsPHPUnit_Framework_TestCase { public functiontestWord() { $this->assertEquals('cheryl', rot13('purely')); } } Test methodhas test prefix Post-condition verification
12. Your first PHPUnittest (3) nicobn@nicobn-laptop:~$ phpunit rot13.php PHPUnit 3.5.5 by Sebastian Bergmann. . Time: 0 seconds, Memory: 3.50Mb OK (1 test, 1 assertion) The test passes but have wereallycovered all of our bases ?
13. Preconditions, invariants and postconditions functionrot13($text) { $len = strlen($text); for ($i = 0; $i < $len; $i++) { $text{$i} = chr((ord($text{$i}) - ord('a') + 13) % 26 + ord('a')); } return$text; } Precondition: $text must be a string Precondition: $text must belower case Invariant: non-alpha characters must remainunchanged Postcondition: each alpha character must bemoved 13 positions Wescrewed up ! How many more tests do weneed ?
14. NEWFLASH: It’s not a question of how awesomelyawesome of a programmer you are, but a question of methodology ! Sad panda issad
15. Test first ! (1) “Applying ROT13 to a piece of text merely requires examining its alphabetic characters and replacing each one by the letter 13 places further along in the alphabet, wrapping back to the beginning if necessary. A becomes N, B becomes O, and so on up to M, which becomes Z, then the sequence continues at the beginning of the alphabet: N becomes A, O becomes B, and so on to Z, which becomes M. Only those letters which occur in the English alphabet are affected; numbers, symbols, whitespace, and all other characters are left unchanged. Because there are 26 letters in the English alphabet and 26 = 2 × 13, the ROT13 function is its own inverse.” (Wikipedia)
16. Test first ! (2a) functionrot13($text) { } class Rot13Test extendsPHPUnit_Framework_TestCase { public function test_A_Lower() { $this->assertEquals('n', rot13('a')); } } There was 1 failure: 1) Rot13Test::test_A_Lower Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -n +
17. Test first ! (2b) functionrot13($text) { return‘n’; } class Rot13Test extendsPHPUnit_Framework_TestCase { public function test_A_Lower() { $this->assertEquals('n', rot13('a')); } } PHPUnit 3.5.5 by Sebastian Bergmann. . Time: 1 second, Memory: 3.50Mb OK (1 test, 1 assertion)
18. Test first ! (2c) functionrot13($text) { returnchr(ord($text{0}) + 13); } class Rot13Test extendsPHPUnit_Framework_TestCase { public function test_A_Lower() { $this->assertEquals('n', rot13('a')); } } PHPUnit 3.5.5 by Sebastian Bergmann. . Time: 1 second, Memory: 3.50Mb OK (1 test, 1 assertion)
19. Test first ! (3a) functionrot13($text) { returnchr(ord($text{0}) + 13); } class Rot13Test extendsPHPUnit_Framework_TestCase { public function test_A_Lower() { $this->assertEquals('n', rot13('a')); } public function test_N_Lower() { $this->assertEquals(‘a', rot13(‘n')); } } FAIL !
20. Test first ! (3b) functionrot13($text) { return chr((ord($text{0}) - ord('a') + 13) % 26 + ord('a')); } class Rot13Test extendsPHPUnit_Framework_TestCase { public function test_A_Lower() { $this->assertEquals('n', rot13('a')); } public function test_N_Lower() { $this->assertEquals(‘a', rot13(‘n')); } } PASS !
21. Test first ! (4a) functionrot13($text) { return chr((ord($text{0}) - ord('a') + 13) % 26 + ord('a')); } class Rot13Test extendsPHPUnit_Framework_TestCase { public function test_A_Lower() { $this->assertEquals('n', rot13('a')); } public function test_N_Lower() { $this->assertEquals(‘a', rot13(‘n')); } public functiontest_Symbol() { $this->assertEquals('$', rot13('$')); } } FAIL !
22. Test first ! (4b) functionrot13($text) { if (!ctype_alnum($text{0})) { return$text{0}; } return chr((ord($text{0}) - ord('a') + 13) % 26 + ord('a')); } class Rot13Test extendsPHPUnit_Framework_TestCase { public function test_A_Lower() { $this->assertEquals('n', rot13('a')); } public function test_N_Lower() { $this->assertEquals(‘a', rot13(‘n')); } public functiontest_Symbol() { $this->assertEquals('$', rot13('$')); } } PASS !
23. Test first ! (5a) functionrot13($text) { if (!ctype_alnum($text{0})) { return$text{0}; } return chr((ord($text{0}) - ord('a') + 13) % 26 + ord('a')); } class Rot13Test extendsPHPUnit_Framework_TestCase { /* […] */ public functiontest_N_Upper() { $this->assertEquals('A', rot13('N')); } } FAIL !
24. Test first ! (5b) functionrot13($text) { if (!ctype_alnum($text{0})) { return$text{0}; } if (ctype_upper($text{0})) { $delta = ord('A'); } else { $delta = ord('a'); } return chr((ord($text{0}) - $delta + 13) % 26 + $delta); } class Rot13Test extendsPHPUnit_Framework_TestCase { /* […] */ public functiontest_N_Upper() { $this->assertEquals('A', rot13('N')); } } PASS !
25. Test first ! (6a) functionrot13($text) { if (!ctype_alnum($text{0})) { return$text{0}; } if (ctype_upper($text{0})) { $delta = ord('A'); } else { $delta = ord('a'); } return chr((ord($text{0}) - $delta + 13) % 26 + $delta); } class Rot13Test extendsPHPUnit_Framework_TestCase { /* […] */ public functiontest_N_Purely() { $this->assertEquals(‘$purely$', rot13(‘$cheryl$')); } } FAIL !
29. Test status Do yourself and yourcolleaguesa favor and mark tests as skipped or incompletewhen relevant !
30. Anatomy of a unit test SETUP: Create the fixture EXERCISE: Execute the system under test (SUT) VERIFY: Check expectations (state mutations, output, invariants, etc.) TEAR DOWN: Clean the fixture
36. Should not harm the developmentprocess in the long runTests, if usedincorrectly, canbedetrimental to a project
37. Principles of test automation 1) INDEPENDANCE: A test should not interactwithanother test (shared mutable state). 2) NO TEST CODE IN PRODUCTION: Ban if ($test). Don’teventhink about it. 3) ISOLATION: Each SUT must beindependant. State mutations duringexerciseshould onlyoccur in and by the SUT. 4) D.R.Y.: Do not repeatyourself. Valid for fixture setup as well as test overlap. 5) CLEAR INTENTIONS: A test shouldbe simple and clearlycommunicateits scope. 6) MINIMIZE UNTESTABLE CODE: Sorry, please replace minimize by eliminate. 7) TEST FIRST: Preventsmostsmells, ensureshighcoverage, providesimmediate feedback. 8) DO NOT TEST YOUR PRIVATES: The need to test a privatemethodis a symptom of a deeperproblem.
38. Exempligratia Symfony2’s routing component: https://github.com/symfony/symfony/blob/master/tests/Symfony/Tests/Component/Routing/Matcher/UrlMatcherTest.php Example of a feeble test (objectcreation code repeated), long test (testMatch). Symfony2’s DOM Crawler: https://github.com/symfony/symfony/blob/master/tests/Symfony/Tests/Component/DomCrawler/CrawlerTest.php Example of utility methods (at the bottom), @covers, SUT factorymethod (createTestCrawler). Symfony2’s HttpKernel component: https://github.com/symfony/symfony/blob/master/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php Example of test doubles.
39. Resources The seminal book on unit testing. Written by Gerard Meszaros. ISBN-13: 978-0131495050