Slide 1

Slide 1 text

MYTH CODE UNTESTABLE THE OF

Slide 2

Slide 2 text

SEBASTIAN BERGMANN SEBASTIAN HEUER CREATOR OF PHPUNIT CO-FOUNDER OF THEPHP.CC DEVELOPER ADVOCATE DIE KARTENMACHEREI

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

TESTING CLEAN CODE IS EASY

Slide 6

Slide 6 text

Headline LEGACY HELL

Slide 7

Slide 7 text

TEMPLATE NEW

Slide 8

Slide 8 text

template_new.php

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

IDML FO PNG PDF

Slide 11

Slide 11 text

PDF PNG FO

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

1

Slide 17

Slide 17 text

(…) 2777 if ($isTranslationTool === false) { 2778 if (file_exists($configurationPath . 'spot' . $filenameSuffix . '.pdf')) { 2779 if ($isDebugMode) { 2780 echo $configurationPath . 'spot' . $filenameSuffix . '.pdf
'; 2781 echo 'GENERATING PAGES ' . $i . ' spot.pdf
'; 2782 } 2783 $pdf->setSourceFile($configurationPath . 'spot' . $filenameSuffix . '.pdf'); 2784 $tplIdx = $pdf->importPage($i + 1); 2785 $pdf->useTemplate($tplIdx, 0, 0); 2786 } elseif (file_exists($template_path . 'spot.pdf')) { 2787 if ($isDebugMode) { 2788 echo $template_path . 'spot.pdf
'; 2789 echo 'GENERATING PAGES ' . $i . ' spot.pdf
'; 2790 } 2791 $pdf->setSourceFile($template_path . 'spot.pdf'); 2792 $tplIdx = $pdf->importPage($i + 1); 2793 $pdf->useTemplate($tplIdx, 0, 0); 2794 } elseif ($isDebugMode) { 2795 echo 'NOT FOUND >> spot.pdf
'; 2796 } 2797 } (…)

Slide 18

Slide 18 text

"The method execute() has an NPath complexity of 42261380675144684438377688955408190012304275410858 3958093751102384903715343564800000." "The configured NPath complexity threshold is 200."

Slide 19

Slide 19 text

FOUR HUNDRED TWENTY-TWO SEXVIGINTILLION, SIX HUNDRED THIRTEEN QUINVIGINTILLION, EIGHT HUNDRED SIX QUATTUORVIGINTILLION, SEVEN HUNDRED FIFTY-ONE TREVIGINTILLION, FOUR HUNDRED FORTY-SIX DUOVIGINTILLION, EIGHT HUNDRED FORTY-FOUR UNVIGINTILLION, THREE HUNDRED EIGHTY-THREE VIGINTILLION, SEVEN HUNDRED SEVENTY-SIX NOVEMDECILLION, EIGHT HUNDRED EIGHTY-NINE OCTODECILLION, FIVE HUNDRED FIFTY-FOUR SEPTENDECILLION, EIGHTY-ONE SEXDECILLION, NINE HUNDRED QUINDECILLION, ONE HUNDRED TWENTY-THREE QUATTUORDECILLION, FORTY-TWO TREDECILLION, SEVEN HUNDRED FIFTY-FOUR DUODECILLION, ONE HUNDRED EIGHT UNDECILLION, FIVE HUNDRED EIGHTY-THREE DECILLION, NINE HUNDRED FIFTY-EIGHT NONILLION, NINETY-THREE OCTILLION, SEVEN HUNDRED FIFTY-ONE SEPTILLION, ONE HUNDRED TWO SEXTILLION, THREE HUNDRED EIGHTY-FOUR QUINTILLION, NINE HUNDRED THREE QUADRILLION, SEVEN HUNDRED FIFTEEN TRILLION, THREE HUNDRED FORTY-THREE BILLION, FIVE HUNDRED SIXTY-FOUR MILLION, EIGHT HUNDRED THOUSAND

Slide 20

Slide 20 text

GLOBAL STATE A GAZILLION EXECUTION PATHS STATIC METHOD CALLS

Slide 21

Slide 21 text

TESTING LEGACY CODE IS HARD impossible?

Slide 22

Slide 22 text

"LET'S JUST REBUILD THIS FROM SCRATCH!"

Slide 23

Slide 23 text

NO DOCUMENTATION ONLY CODE

Slide 24

Slide 24 text

A Clockwork Orange - Ludovico technique; © Warner Bros

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

UNIT do we write the software right?

Slide 27

Slide 27 text

TESTING LEGACY CODE IS HARD impossible?

Slide 28

Slide 28 text

TESTING LEGACY CODE IS HARD Unit-testing unrefactored legacy code is impossible

Slide 29

Slide 29 text

END-TO-END TESTING UNREFACTORED LEGACY CODE IS ALWAYS POSSIBLE

Slide 30

Slide 30 text

ACCEPTANCE INTEGRATION UNIT END-TO-END do we write the right software? do we write the software right? execution time $$$ ¢ maintenance cost

Slide 31

Slide 31 text

do we write the right software? END-TO-END $$$

Slide 32

Slide 32 text

IDML FO PNG PDF

Slide 33

Slide 33 text

1 'KAM11GG', 10 'template_format' => 'F210', 11 'template_color' => 'C06', 12 // (...) 13 ]; 14 15 require __DIR__ .'/../src/template_new.php'; 16 17 $expectedFile = __DIR__ . '/fixtures/expected.fo'; 18 $actualFile = __DIR__ . '/../data/templates/KAM11GG/F210/C06/input.fo'; 19 20 $this->assertFileEquals($expectedFile, $actualFile); 21 } 22 }

Slide 34

Slide 34 text

1 $design, 18 'template_format' => $format, 19 'template_color' => $color, 20 // (...) 21 ]; 22 23 require __DIR__ .'/../src/template_new.php'; 24 25 $actualFile = sprintf(__DIR__ . '/../data/templates/%s/%s/%s/input.fo', $design, $format, $color); 26 27 $this->assertFileEquals($expectedFile, $actualFile); 28 } 29 }

Slide 35

Slide 35 text

BUT WHAT IF YOU DON'T KNOW INPUT AND/OR OUTPUT?

Slide 36

Slide 36 text

CHARACTERIZATION TESTS "An attempt to lock existing behavior into an untested or undocumented system." http://wiki.c2.com/?CharacterizationTest

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

EXAMPLE

Slide 39

Slide 39 text

1

Slide 40

Slide 40 text

$ php -d xdebug.auto_trace=1 -d xdebug.trace_format=1 \ -d xdebug.collect_params=5 -d xdebug.collect_return=1 legacy.php $ cat /tmp/trace.4251619279.xt Version: 2.5.3 File format: 4 TRACE START [2014-06-27 10:40:40] 1 0 0 0.000282 279896 {main} 1 /home/sb/legacy.php 0 0 2 1 0 0.000371 280136 add 1 /home/sb/legacy.php 7 2 aToxOw== aToyOw== 2 1 1 0.000440 280256 2 1 R aTozOw== 1 0 1 0.000470 280016 1 0 R aToxOw== 0.000648 8488 TRACE END [2014-06-27 10:40:40] $ de-legacy-fy generate-characterization-test add /tmp/trace.4251619279.xt CharacterizationTest CharacterizationTest.php de-legacy-fy 2.0.0 by Sebastian Bergmann. Generated class "CharacterizationTest" in file "CharacterizationTest.php"

Slide 41

Slide 41 text

1 decode('aTozOw=='), $this->decode('aToxOw=='), $this->decode('aToyOw==')) 19 ); 20 } 21 22 /** 23 * @param string $value 24 * @return mixed 25 */ 26 private function decode($value) 27 { 28 return unserialize(base64_decode($value)); 29 }
 30 31 32 33 34 35 36 37 38 } 31 /** 32 * @dataProvider provider 33 */ 34 public function testAddFunctionWorksLikeItUsedTo($expected, $a, $b) 35 { 36 $this->assertEquals($expected, add($a, $b)); 37 }

Slide 42

Slide 42 text

BACK TO OUR CASE

Slide 43

Slide 43 text

NEXT: REFACTOR TO ALLOW INTEGRATION TESTING

Slide 44

Slide 44 text

MOVE CODE INTO CLASS

Slide 45

Slide 45 text

1 getBasePath(), 0, -1); // remove ending slash 24 $configurationRoot = $oConfig->getConfigByPath('path/configurations'); 25 $templateRoot = ''; // setted up in line 382 (template switch) 26 $sFopConfigRelative = $oConfig->getConfigByPath('path/fop/config'); 27 $sFopConfigAbsolute = $basePath . '/' . $sFopConfigRelative; 28 $usedDieCut = (isset($_REQUEST['diecut']) && !empty($_REQUEST['diecut']) && 'D53' == $_REQUEST['diecut'] ? 29 $_REQUEST['diecut'] : ''); 30 $configurationID = (isset($_REQUEST['configurationID'])) ? $_REQUEST['configurationID'] : null;

Slide 46

Slide 46 text

INTRODUCE DEPENDENCY INJECTION

Slide 47

Slide 47 text

1 config = $config; 17 $this->idmlParser = $idmlParser; 18 $this->request = $request; 19 } 20 21 public function execute() { 22 $configurationIdChecker = new ConfigurationIdChecker(); 23 24 $templateFormatParameter = $this->request->getFormat(); 25 $templateDesignParameter = $this->request->getDesign(); 26 $templateColorParameter = $this->request->getColor(); 27 28 $basePath = substr($this->config->getBasePath(), 0, -1); // remove ending slash 29 $configurationRoot = $this->config->getConfigByPath('path/configurations'); 30 $templateRoot = ''; // setted up in line 382 (template switch) 31 $sFopConfigRelative = $this->config->getConfigByPath('path/fop/config');

Slide 48

Slide 48 text

ACCEPTANCE INTEGRATION END-TO-END do we write the right software? do we write the software right? execution time $$$ ¢ maintenance cost

Slide 49

Slide 49 text

USE TESTS TO WRITE NEW CODE

Slide 50

Slide 50 text

CODE COVERAGE REPORT

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

Webserver Legacy Code FO

Slide 54

Slide 54 text

Webserver Legacy Code New Code Abstraction Layer FO

Slide 55

Slide 55 text

Webserver Legacy Code New Code Abstraction Layer $format === 'F040' FO

Slide 56

Slide 56 text

BRANCH BY ABSTRACTION

Slide 57

Slide 57 text

SUMMARY

Slide 58

Slide 58 text

MEANINGFUL TESTS ARE A VALUABLE SOURCE OF DOCUMENTATION

Slide 59

Slide 59 text

INTEGRATION TESTS ALLOW YOU TO WRITE NEW, CLEAN CODE

Slide 60

Slide 60 text

USE BRANCH BY ABSTRACTION TO GRADUALLY MOVE FUNCTIONALITY TO NEW CODE

Slide 61

Slide 61 text

WRITE UNIT TESTS FOR NEW CODE

Slide 62

Slide 62 text

TESTING CLEAN CODE IS EASY

Slide 63

Slide 63 text

THANK YOU! @inside_kam @belanur @thePHPcc @s_bergmann