let your tests drive your code

137
Let your tests drive your development An in2it workshop in it 2 PROFESSIONAL PHP SERVICES

Upload: michelangelo-van-dam

Post on 22-Jan-2018

497 views

Category:

Engineering


3 download

TRANSCRIPT

Page 1: Let your tests drive your code

Let your tests drive your development

An in2it workshop

in it2PROFESSIONAL PHP SERVICES

Page 2: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Agenda

2

Introduction

TDD from scratch

TDD with legacy app

Additional tips

Recap

Page 3: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Agenda

3

Introduction

TDD from scratch

TDD with legacy app

Additional tips

Recap

Page 4: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

What is test-driven development (TDD)?Write unit tests first

They will fail

Write functional code in accordance of the tests

Your tests will structure the way you write your code

Re-run your tests again

They should pass

4

Page 5: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

System’s Check5

Page 6: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Some conventionsPHPUnit was installed using composer

All vendor packages were installed with the code base

Running PHPUnit with ./vendor/bin/phpunit

If you use a different approach, make sure it works for you

GIT is used for the exercises, make sure you know about

checking out branches

reading git logs

In the slides I left out comments to save space

6

Page 7: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleFunctional requirement

Write a small PHP class with a method that will return the string “Hello World!”

7

Page 8: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple examplePHPUnit test

<?php

namespace App\Test;

use PHPUnit\Framework\TestCase; use App\HelloWorld;

class HelloWorldTest extends TestCase {

public function testAppOutputsHelloWorld() { $helloWorld = new HelloWorld(); $expectedAnswer = $helloWorld->sayHello(); $this->assertSame('Hello World!', $expectedAnswer); } }

8

Page 9: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleRunning unit tests

9

Page 10: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleWriting the code

<?php

namespace App;

class HelloWorld { public function sayHello(): string { return 'Hello World!'; } }

10

Page 11: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleRe-run unit tests

11

Page 12: Let your tests drive your code
Page 13: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Change requests

13

Page 14: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleChange request

Update our small PHP class method that will allow an argument and will return the string “Hello <arg>!” where <arg> is the argument provided.

14

Page 15: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleOptions

15

Page 16: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleWhy change the existing test?

Code change so test has to change

New requirements change the test goal

16

Page 17: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleWhy not change the existing test?

Code requirement change so new test is required

We don’t want to change existing requirements

Prevent BC breaks

New test will cover changing requirements

17

Page 18: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleNew test case

public function testAppOutputsHelloArgument() { $helloWorld = new HelloWorld(); $expectedAnswer = $helloWorld->sayHello('unit testers'); $this->assertSame('Hello unit testers!', $expectedAnswer); }

18

Page 19: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleRunning unit tests

19

Page 20: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple example Changing the code

<?php

namespace App;

class HelloWorld { public function sayHello(string $arg): string { return 'Hello ' . $arg . '!'; } }

20

Page 21: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleRe-run the tests

21

Page 22: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

We introduced an error now!

22

Page 23: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleFinding the bug

<?php

namespace App;

class HelloWorld { public function sayHello(string $arg): string { return 'Hello ' . $arg . '!'; } }

23

No default value

Page 24: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleFixing failure

<?php

namespace App;

class HelloWorld { public function sayHello(string $arg = 'World'): string { return 'Hello ' . $arg . '!'; } }

24

Page 25: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleRe-run the tests

25

Page 26: Let your tests drive your code
Page 27: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleRecap

Write test based on functionality (and run test)

Write code based on functionality (and re-run test)

Write new test based on changed functionality (and re-run tests)

Change code based on functionality (and re-run tests)

Update code until all tests are passing

27

Page 28: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Simple exampleGet the source code

Go to the project folder and use the following commands gitcheckoutsimple-example./vendor/bin/phpunit

All files will be there, so review them closely

28

Page 29: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development29

ExerciseNew change requirements

Security is important, so we need to validate the given argument so it only accepts string type values. If something other than a string is provided, an exception should be raised.

10 minutes

Page 30: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Agenda

30

Introduction

TDD from scratch

TDD with legacy app

Additional tips

Recap

Page 31: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Project “TodoToDone”Project “TodoToDone” is a simple todo tool, tracking the tasks that you need to do. It should provide the following features:

List open tasks sorted newest to oldest

Create a new task (label and description)

Update an existing task

Mark task as done in the overview list

Remove task marked as done

31

Page 32: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Requirements as skeleton<?php

namespace App\Test\Service;

use PHPUnit\Framework\TestCase;

class TaskServiceTest extends TestCase {     public function testServiceReturnsListOfTasks()     {         // List open tasks sorted newest to oldest     }

    public function testServiceCanAddNewTask()     {         // Create a new task (label and description)     }

    public function testServiceCanUpdateExistingTask()     {         // Update an existing task     }

    public function testServiceCanMarkTaskAsDone()     {         // Mark task as done in the overview list     }

    public function testServiceCanRemoveTaskMarkedAsDone()     {         // Remove task marked as done     } }

32

Page 33: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Don’t start writing test yet!Unit testing is about looking at a specific task from every angle

Define use and edge cases and add them as additional tests

33

Page 34: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Example edge casespublic function testServiceWillThrowRuntimeExceptionWhenStorageFailsToFetchTaskList() {     // Throw a runtime exception when connection to storage fails for fetching task list }

public function testServiceWillThrowInvalidArgumentExceptionWhenInvalidTaskIsAdded() {     // Throw an invalid argument exception for invalid task when adding }

public function testServiceWillThrowRuntimeExceptionWhenStorageFails() {     // Throw a runtime exception when storage of task fails }

public function testServiceWillThrowDomainExceptionWhenTaskWasMarkedAsDoneWhenMarkingTaskAsDone() {     // Throw a domain exception when a task was already marked as done  }

34

Page 35: Let your tests drive your code

QuestionWhy am I using very long and explicit method names for

my test methods?

Page 36: Let your tests drive your code

AnswerTo have human readable documentation about the features we’re developing and testing.

./vendor/bin/phpunit--testdox

Page 37: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

TestDox outputPHPUnit 6.1.3 by Sebastian Bergmann and contributors.

App\Test\Service\TaskService [ ] Service returns list of tasks [ ] Service can add new task [ ] Service throws exception if task was not found [ ] Service can find task [ ] Service can remove task [ ] Service can update existing task [ ] Service can mark task as done [ ] Service can remove task marked as done [ ] Service will throw type error when invalid task is added [ ] Service will throw domain exception when done task gets marked done

37

Page 38: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development38

ExerciseComplete the test cases

gitcheckouttdd-ex1

Go and check out branch tdd-ex1 where you will find the code as we’ve seen thus far.

Pro tip: complete 1 test and commit, this way you also learn to commit small and commit often.

20 minutes

Page 39: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

How we’re approaching thisWe need to “prepare” our test class

Create a “setUp” method to create a fixture

Create a “tearDown” method to unset the fixture

Implement first test “testServiceReturnsListOfTasks”

Making use of fixture to mimic actual behaviour

Create class interfaces for structure

Implement concrete class

39

Page 40: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Preparing our 1st test

40

Page 41: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

class TaskServiceTest extends TestCase {     protected $taskGateway;

    protected function setUp()     {         parent::setUp();

        $taskEntity = $this->getMockBuilder(TaskEntityInterface::class)             ->setMethods(['getId', 'getLabel', 'getDescription', 'isDone', 'getCreated', 'getModified'])->getMock();

        $taskEntry1 = clone $taskEntity;         $taskEntry1->method('getId')->willReturn('123');         $taskEntry1->method('getLabel')->willReturn('Task #123');         $taskEntry1->method('getDescription')->willReturn('#123: This is task 123');         $taskEntry1->method('isDone')->willReturn(false);         $taskEntry1->method('getCreated')->willReturn(new \DateTime('2017-03-21 07:53:24'));         $taskEntry1->method('getModified')->willReturn(new \DateTime('2017-03-21 08:16:53'));

        $taskEntry2 = clone $taskEntity;         $taskEntry3 = clone $taskEntity;

        $taskCollection = new \SplObjectStorage();         $taskCollection->attach($taskEntry3);         $taskCollection->attach($taskEntry2);         $taskCollection->attach($taskEntry1);

        $taskGateway = $this->getMockBuilder(TaskGatewayInterface::class)->setMethods(['fetchAll'])->getMock();         $taskGateway->expects($this->any())->method('fetchAll')->willReturn($taskCollection);         $this->taskGateway = $taskGateway;     }     /* ... */ }

41

Page 42: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

class TaskServiceTest extends TestCase {     /* ... */

    protected function tearDown()     {         unset ($this->taskGateway);     }

    /* ... */ }

42

Page 43: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

class TaskServiceTest extends TestCase {     /* ... */

    /**      * List open tasks sorted newest to oldest      *      * @covers TaskService::fetchAll      */     public function testServiceReturnsListOfTasks()     {         $taskService = new TaskService($this->taskGateway);         $taskList = $taskService->getAllTasks();

        $this->assertInstanceOf(\Iterator::class, $taskList);         $this->assertGreaterThan(0, count($taskList));         $taskList->rewind();         $previous = null;         while ($taskList->valid()) {             if (null !== $previous) {                 $current = $taskList->current();                 $this->assertTrue($previous->getCreated() > $current->getCreated());             }             $previous = $taskList->current();             $taskList->next();         }     }

    /* ... */ }

43

Page 44: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Creating gateway interface<?php

namespace App\Model;

interface TaskGatewayInterface {     /**      * Fetch all tasks from the back-end storage      * @return \Iterator      */     public function fetchAll(): \Iterator; }

44

Page 45: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Creating entity interface<?php

namespace App\Model;

interface TaskEntityInterface {     public function getId(): string;     public function getLabel(): string;     public function getDescription(): string;     public function isDone(): bool;     public function getCreated(): \DateTime;     public function getModified(): \DateTime; }

45

Page 46: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development46

Page 47: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development47

ExerciseCheck out the finished test cases

gitcheckouttdd-ex2

Go and check out branch tdd-ex2 where you will find the completed test cases.

Make sure you also check out the GIT logs as I used 27 commits to explain what was happening and why!

10 minutes

Page 48: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

protected function setUp() { parent::setUp();

// We create a mock object $taskEntity = $this->getMockBuilder(TaskEntityInterface::class) ->setMethods(['getId', 'getLabel', 'getDescription', 'isDone', 'getCreated', 'getModified', 'setLabel', 'setDone']) ->getMock();

$taskEntry1 = clone $taskEntity; $taskEntry1->method('getId')->willReturn('123'); $taskEntry1->method('getLabel')->willReturn('Task #123'); $taskEntry1->method('getDescription')->willReturn('#123: This is task 123'); $taskEntry1->method('isDone')->willReturn(false); $taskEntry1->method('getCreated')->willReturn(new \DateTime('2017-03-21 07:53:24')); $taskEntry1->method('getModified')->willReturn(new \DateTime('2017-03-21 08:16:53'));

$taskEntryUpdate = clone $taskEntity; $taskEntryUpdate->method('getId')->willReturn('123'); $taskEntryUpdate->method('getLabel')->willReturn('Task #123: Update from service'); $taskEntryUpdate->method('getDescription')->willReturn('#123: This is task 123'); $taskEntryUpdate->method('isDone')->willReturn(false); $taskEntryUpdate->method('getCreated')->willReturn(new \DateTime('2017-03-21 07:53:24')); $taskEntryUpdate->method('getModified')->willReturn(new \DateTime('now'));

$taskEntry2 = clone $taskEntity; $taskEntry2->method('getId')->willReturn('456'); $taskEntry2->method('getLabel')->willReturn('Task #456'); $taskEntry2->method('getDescription')->willReturn('#456: This is task 456'); $taskEntry2->method('isDone')->willReturn(true); $taskEntry2->method('getCreated')->willReturn(new \DateTime('2017-03-22 07:53:24')); $taskEntry2->method('getModified')->willReturn(new \DateTime('2017-03-22 08:16:53'));

$taskEntry3 = clone $taskEntity; $taskEntry3->method('getId')->willReturn('789'); $taskEntry3->method('getLabel')->willReturn('Task #789'); $taskEntry3->method('getDescription')->willReturn('#789: This is task 789'); $taskEntry3->method('isDone')->willReturn(false); $taskEntry3->method('getCreated')->willReturn(new \DateTime('2017-04-23 07:53:24')); $taskEntry3->method('getModified')->willReturn(new \DateTime('2017-04-23 08:16:53'));

$taskEntryDone = clone $taskEntity; $taskEntryDone->method('getId')->willReturn('789'); $taskEntryDone->method('getLabel')->willReturn('#789'); $taskEntryDone->method('getDescription')->willReturn('#789: This is task 789'); $taskEntryDone->method('isDone')->willReturn(true); $taskEntryDone->method('getCreated')->willReturn(new \DateTime('2017-04-23 07:53:24')); $taskEntryDone->method('getModified')->willReturn(new \DateTime('now'));

48

Page 49: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Magic happening in setUpIdeal place to set things up (using fixtures)

Stub is shared among different test methods

Now all is ready to be implemented as we secured the code-base

49

Page 50: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Project statusApp\Test\Service\TaskService [x] Service returns list of tasks [x] Service can add new task [x] Service throws exception if task was not found [x] Service can find task [x] Service can remove task [x] Service can update existing task [x] Service can mark task as done [x] Service can remove task marked as done [x] Service will throw type error when invalid task is added [x] Service will throw domain exception when done task gets marked done

50

Page 51: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development51

ExerciseCheck out the finished test cases

gitcheckouttdd-ex3

Go and check out branch tdd-ex3 where you will find the other completed test cases.

15 minutes

Page 52: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development52

Page 53: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

TextDox outputPHPUnit 6.1.3 by Sebastian Bergmann and contributors.

App\Test\Model\TaskEntity [x] Task entity is empty at construction [x] Task entity throws error when constructed with wrong type of arguments [x] Task entity throws exception when constructed with wrong arguments [x] Task entity accepts correct arguments

App\Test\Model\TaskGateway [x] Fetch all returns iterator object [x] Gateway can add task entity [x] Find returns null when nothing found [x] Find returns task entity when result is found

[x] Gateway can remove task entity [x] Gateway can update task entity

App\Test\Service\TaskService [x] Service returns list of tasks [x] Service can add new task [x] Service throws exception if task was not found [x] Service can find task [x] Service can remove task [x] Service can update existing task [x] Service can mark task as done [x] Service can remove task marked as done [x] Service will throw type error when invalid task is added [x] Service will throw domain exception when done task gets marked done

53

Page 54: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development54

ExerciseCheck out the web app

gitcheckouttdd-ex4php-Slocalhost:8000-twebweb/index.php

Go and check out branch tdd-ex4 where you will find the other completed test cases.

Run the web application using PHP’s build-in web server to see how the app is behaving.

15 minutes

Page 55: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development55

Page 56: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development56

Page 57: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development57

Page 58: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development58

Page 59: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development59

ConclusionLet’s recap what has happened here

Writing test-first gives you a clean scope of what your code should do.

You have a more precise code-base that’s easy to maintain, upgrade and is independent.

Page 60: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Agenda

60

Introduction

TDD from scratch

TDD with legacy app

Additional tips

Recap

Page 61: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Legacy challengesNot (always) written with testing in mind

Dependencies make it hard to change code

Refactoring is often required before proper testing can start

For refactoring tests are required to ensure the refactored code behaves the same!

61

Page 62: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Example project: EPESI

62

Page 63: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development63

Page 64: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Tests?!?

64

Page 65: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Tests?!?

64

Page 66: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

How to get started?

65

Page 67: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Getting ready to test!<?xmlversion="1.0"encoding="UTF-8"?>

<phpunitbootstrap="vendor/autoload.php"colors="true"stopOnErrors="true"stopOnFailures="true">

<testsuites><testsuitename="Appunittests"><directorysuffix="php">tests</directory></testsuite></testsuites>

<filter><whitelist><directorysuffix="php">src</directory></whitelist></filter>

<logging><logtype="coverage-html"target="build/coverage"lowUpperBound="35"highLowerBound="70"/></logging>

</phpunit>

66

Page 68: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

ModuleManager::module_install/**  * Includes file with module installation class.  *  * Do not use directly.  *  * @param string $module_class_name module class name - underscore separated  */ public static final function include_install($module_class_name) {     if(isset(self::$modules_install[$module_class_name])) return true;     $path = self::get_module_dir_path($module_class_name);     $file = self::get_module_file_name($module_class_name);     $full_path = 'modules/' . $path . '/' . $file . 'Install.php';     if (!file_exists($full_path)) return false;     ob_start();     $ret = require_once($full_path);     ob_end_clean();     $x = $module_class_name.'Install';     if(!(class_exists($x, false)) ||  !array_key_exists('ModuleInstall',class_parents($x)))         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);     self::$modules_install[$module_class_name] = new $x($module_class_name);     return true; }

67

Page 69: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Testing happily…<?php

use PHPUnit\Framework\TestCase;

require_once __DIR__ . '/../src/ModuleManager.php';

class ModuleManagerTest extends TestCase {     /**      * @covers ModuleManager::include_install      */     public function testModuleManagerCanLoadMailModule()     {         $result = \ModuleManager::include_install('Mail');         $this->assertTrue($result);     } }

68

Page 70: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development69

Page 71: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Comment out the test<?php

use PHPUnit\Framework\TestCase;

require_once __DIR__ . '/../src/ModuleManager.php';

class ModuleManagerTest extends TestCase {     /**      * @covers ModuleManager::include_install      */     /*public function testModuleManagerCanLoadMailModule()     {         $result = \ModuleManager::include_install('Mail');         $this->assertTrue($result);     }*/

}

70

Page 72: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Let’s look again, more closely/**  * Includes file with module installation class.  *  * Do not use directly.  *  * @param string $module_class_name module class name - underscore separated  */ public static final function include_install($module_class_name) {     if(isset(self::$modules_install[$module_class_name])) return true;     $path = self::get_module_dir_path($module_class_name);     $file = self::get_module_file_name($module_class_name);     $full_path = 'modules/' . $path . '/' . $file . 'Install.php';     if (!file_exists($full_path)) return false;     ob_start();     $ret = require_once($full_path);     ob_end_clean();     $x = $module_class_name.'Install';     if(!(class_exists($x, false)) ||  !array_key_exists('ModuleInstall',class_parents($x)))         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);     self::$modules_install[$module_class_name] = new $x($module_class_name);     return true; }

71

Page 73: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Let’s look again, more closely/**  * Includes file with module installation class.  *  * Do not use directly.  *  * @param string $module_class_name module class name - underscore separated  */ public static final function include_install($module_class_name) {     if(isset(self::$modules_install[$module_class_name])) return true;     $path = self::get_module_dir_path($module_class_name);     $file = self::get_module_file_name($module_class_name);     $full_path = 'modules/' . $path . '/' . $file . 'Install.php';     if (!file_exists($full_path)) return false;     ob_start();     $ret = require_once($full_path);     ob_end_clean();     $x = $module_class_name.'Install';     if(!(class_exists($x, false)) ||  !array_key_exists('ModuleInstall',class_parents($x)))         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);     self::$modules_install[$module_class_name] = new $x($module_class_name);     return true; }

71

Page 74: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Let’s look again, more closely/**  * Includes file with module installation class.  *  * Do not use directly.  *  * @param string $module_class_name module class name - underscore separated  */ public static final function include_install($module_class_name) {     if(isset(self::$modules_install[$module_class_name])) return true;     $path = self::get_module_dir_path($module_class_name);     $file = self::get_module_file_name($module_class_name);     $full_path = 'modules/' . $path . '/' . $file . 'Install.php';     if (!file_exists($full_path)) return false;     ob_start();     $ret = require_once($full_path);     ob_end_clean();     $x = $module_class_name.'Install';     if(!(class_exists($x, false)) ||  !array_key_exists('ModuleInstall',class_parents($x)))         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);     self::$modules_install[$module_class_name] = new $x($module_class_name);     return true; }

71

Page 75: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Let’s look again, more closely/**  * Includes file with module installation class.  *  * Do not use directly.  *  * @param string $module_class_name module class name - underscore separated  */ public static final function include_install($module_class_name) {     if(isset(self::$modules_install[$module_class_name])) return true;     $path = self::get_module_dir_path($module_class_name);     $file = self::get_module_file_name($module_class_name);     $full_path = 'modules/' . $path . '/' . $file . 'Install.php';     if (!file_exists($full_path)) return false;     ob_start();     $ret = require_once($full_path);     ob_end_clean();     $x = $module_class_name.'Install';     if(!(class_exists($x, false)) ||  !array_key_exists('ModuleInstall',class_parents($x)))         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);     self::$modules_install[$module_class_name] = new $x($module_class_name);     return true; }

71

Page 76: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Let’s look again, more closely/**  * Includes file with module installation class.  *  * Do not use directly.  *  * @param string $module_class_name module class name - underscore separated  */ public static final function include_install($module_class_name) {     if(isset(self::$modules_install[$module_class_name])) return true;     $path = self::get_module_dir_path($module_class_name);     $file = self::get_module_file_name($module_class_name);     $full_path = 'modules/' . $path . '/' . $file . 'Install.php';     if (!file_exists($full_path)) return false;     ob_start();     $ret = require_once($full_path);     ob_end_clean();     $x = $module_class_name.'Install';     if(!(class_exists($x, false)) ||  !array_key_exists('ModuleInstall',class_parents($x)))         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);     self::$modules_install[$module_class_name] = new $x($module_class_name);     return true; }

71

Page 77: Let your tests drive your code

http

s://w

ww.

flick

r.com

/pho

tos/

mar

cgbx

/780

3086

292

Page 78: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Test first condition/**   * @covers ModuleManager::include_install   */  public function testReturnImmediatelyWhenModuleAlreadyLoaded()  {      $module = 'Foo_Bar';      ModuleManager::$modules_install[$module] = 1;      $result = ModuleManager::include_install($module);      $this->assertTrue($result);      $this->assertCount(1, ModuleManager::$modules_install);  }

73

Page 79: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development74

Page 80: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development75

Page 81: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development76

http

s://w

ww.

flick

r.com

/pho

tos/

chris

tian_

joha

nnes

en/2

2482

4478

6

Page 82: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Test second condition/**   * @covers ModuleManager::include_install   */  public function testReturnWhenModuleIsNotFound()  {      $module = 'Foo_Bar';      $result = ModuleManager::include_install($module);      $this->assertFalse($result);      $this->assertEmpty(ModuleManager::$modules_install);  }

77

Page 83: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development78

Page 84: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

/**  * Includes file with module installation class.  *  * Do not use directly.  *  * @param string $module_class_name module class name - underscore separated  */ public static final function include_install($module_class_name) {     if(isset(self::$modules_install[$module_class_name])) return true;     $path = self::get_module_dir_path($module_class_name);     $file = self::get_module_file_name($module_class_name);     $full_path = 'modules/' . $path . '/' . $file . 'Install.php';     if (!file_exists($full_path)) return false;     ob_start();     $ret = require_once($full_path);     ob_end_clean();     $x = $module_class_name.'Install';     if(!(class_exists($x, false)) ||  !array_key_exists('ModuleInstall',class_parents($x)))         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);     self::$modules_install[$module_class_name] = new $x($module_class_name);     return true; }

79

Page 85: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

/**  * Includes file with module installation class.  *  * Do not use directly.  *  * @param string $module_class_name module class name - underscore separated  */ public static final function include_install($module_class_name) {     if(isset(self::$modules_install[$module_class_name])) return true;     $path = self::get_module_dir_path($module_class_name);     $file = self::get_module_file_name($module_class_name);     $full_path = 'modules/' . $path . '/' . $file . 'Install.php';     if (!file_exists($full_path)) return false;     ob_start();     $ret = require_once($full_path);     ob_end_clean();     $x = $module_class_name.'Install';     if(!(class_exists($x, false)) ||  !array_key_exists('ModuleInstall',class_parents($x)))         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);     self::$modules_install[$module_class_name] = new $x($module_class_name);     return true; }

79

self::$modules_install[$module_class_name]

Page 86: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Add a “setUp” methodclass ModuleManagerTest extends TestCase {     protected function setUp()     {         ModuleManager::$modules_install = [];     }          /* ... */ }

80

Page 87: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development81

Page 88: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development82

http

s://w

ww.

flick

r.com

/pho

tos/

evae

kebl

ad/1

4780

0905

50

Page 89: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Testing third condition/**  * @covers ModuleManager::include_install  * @expectedException Error  */ public function testTriggerErrorWhenInstallClassDoesNotExists() {     $module = 'EssClient';     $result = ModuleManager::include_install($module);     $this->fail('Expecting loading module ' . $module . ' would trigger an error'); }

83

Page 90: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development84

Page 91: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

/**  * Includes file with module installation class.  *  * Do not use directly.  *  * @param string $module_class_name module class name - underscore separated  */ public static final function include_install($module_class_name) {     if(isset(self::$modules_install[$module_class_name])) return true;     $path = self::get_module_dir_path($module_class_name);     $file = self::get_module_file_name($module_class_name);     $full_path = 'modules/' . $path . '/' . $file . 'Install.php';     if (!file_exists($full_path)) return false;     ob_start();     $ret = require_once($full_path);     ob_end_clean();     $x = $module_class_name.'Install';     if(!(class_exists($x, false)) ||  !array_key_exists('ModuleInstall',class_parents($x)))         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);     self::$modules_install[$module_class_name] = new $x($module_class_name);     return true; }

85

Page 92: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

/**  * Includes file with module installation class.  *  * Do not use directly.  *  * @param string $module_class_name module class name - underscore separated  */ public static final function include_install($module_class_name) {     if(isset(self::$modules_install[$module_class_name])) return true;     $path = self::get_module_dir_path($module_class_name);     $file = self::get_module_file_name($module_class_name);     $full_path = 'modules/' . $path . '/' . $file . 'Install.php';     if (!file_exists($full_path)) return false;     ob_start();     $ret = require_once($full_path);     ob_end_clean();     $x = $module_class_name.'Install';     if(!(class_exists($x, false)) ||  !array_key_exists('ModuleInstall',class_parents($x)))         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);     self::$modules_install[$module_class_name] = new $x($module_class_name);     return true; }

85

if (!file_exists($full_path)) return false;

Page 93: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Our current file structure|-- ModuleManager.php `-- modules     |-- EssClient     |   `-- EssClient.php     |-- IClient     |   `-- IClientInstall.php     `-- Mail         `-- MailInstall.php

86

Page 94: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development87

http

s://w

ww.

flick

r.com

/pho

tos/

sis/

2497

9123

43

Dead Code

Page 95: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

So this test…/**  * @covers ModuleManager::include_install  * @expectedException Error  */ public function testTriggerErrorWhenInstallClassDoesNotExists() {     $module = 'EssClient';     $result = ModuleManager::include_install($module);     $this->fail('Expecting loading module ' . $module . ' would trigger an error'); }

88

Page 96: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

… changes into this test/**  * @covers ModuleManager::include_install  */ public function testTriggerErrorWhenInstallClassDoesNotExists() {     $module = 'EssClient';     $result = ModuleManager::include_install($module);     $this->assertFalse($result); }

89

Page 97: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development90

http

s://w

ww.

flick

r.com

/pho

tos/

fragi

lete

nder

/533

2586

299

Page 98: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Our current file structure|-- ModuleManager.php `-- modules     |-- EssClient     |   `-- EssClient.php     |-- IClient     |   `-- IClientInstall.php     `-- Mail         `-- MailInstall.php

91

Page 99: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Testing fourth condition/**  * @covers ModuleManager::include_install  * @expectedException Error  */ public function testTriggerErrorWhenInstallClassIsNotRegistered() {     $module = 'IClient';     $result = ModuleManager::include_install($module);     $this->fail('Expecting loading module ' . $module . ' would trigger an error'); }

92

Page 100: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development93

Page 101: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development94

Page 102: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Completing all tests

95

Page 103: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Remove comment tags/**  * @covers ModuleManager::include_install  */ public function testModuleManagerCanLoadMailModule() {     $result = \ModuleManager::include_install('Mail');     $this->assertTrue($result); }

96

Page 104: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development97

Page 105: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development98

Page 106: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

RecapTesting legacy code is not easy, but still possible

Approach the source-code with a bail-first approach

Make sure you can “bail” the method as fast as possible

Start with the most important part of your code

Ask yourself “What costs us money if it breaks” ➡ test that first!

99

Page 107: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development100

ExerciseCheck out these tests

gitcheckoutlegacy-0.1

Go and check out branch legacy-0.1 and analyse the tests.

If you have XDebug installed, you can run PHPUnit with code coverage.

15 minutes

Page 108: Let your tests drive your code

What to do?If your code doesn’t return values

Page 109: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

/**  * Process Bank Payment files  */ public function processBankPayments() {     $this->getLogger()->log('Starting bank payment process', Zend_Log::INFO);     foreach ($this->_getBankFiles() as $bankFile) {         $bankData = $this->_processBankFile($bankFile);         $this->getLogger()->log('Processing ' . $bankData->transactionId, Zend_Log::DEBUG);         /** @var Contact_Model_Contact $contact */         $contact = $this->getMapper('Contact_Model_Mapper_Contact')             ->findContactByBankAccount($bankData->transactionAccount);         if (null !== $contact) {             $this->getLogger()->log(sprintf('Found contact "%s" for bank account %s',                  $contact->getName(),$bankData->transactionAccount             ), Zend_Log::DEBUG);             $data = array (                 'amount' => $bankData->transactionAmount,                 'payment_date' => $bankData->transactionDate             );             $this->getMapper('Invoice_Model_Mapper_Payments')                 ->updatePayment($data, array ('contact_id = ?' => $contact->getContactId()));             $this->_moveBankFile($bankFile,                 $this->getPath() . DIRECTORY_SEPARATOR . self::PROCESS_SUCCEEDED             );         } else {             $this->getLogger()->log(sprintf(                 'Could not match bankaccount "%s" with a contact',                 $bankData->transactionAccount             ), Zend_Log::WARN);             $this->_moveBankFile($bankFile,                 $this->getPath() . DIRECTORY_SEPARATOR . self::PROCESS_FAILED             );         }     } }

102

Page 110: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development103

Page 111: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

class Payments_Service_As400Test extends \PHPUnit\Framework\TestCase {     public function testProcessingBankPayments()     {         $contact = $this->getMockBuilder(Contact_Model_Contact::class)             ->setMethods(['getContactId', 'getName'])             ->getMock();

        $contact->expects($this->any())             ->method('getContactId')             ->will($this->returnValue(1));

        $contact->expects($this->any())             ->method('getName')             ->will($this->returnValue('Foo Bar'));

        $contactMapper = $this->getMockBuilder(Contact_Model_Mapper_Contact::class)             ->setMethods(['findContactByBankAccount'])             ->getMock();

        $contactMapper->expects($this->any())             ->method('findContactByBankAccount')             ->will($this->returnValue($contact));

        $paymentsMapper = $this->getMockBuilder(Invoice_Model_Mapper_Payments::class)             ->setMethods(['updatePayment'])             ->getMock();

        $logMock = new Zend_Log_Writer_Mock();         $logger = new Zend_Log();         $logger->setWriter($logMock);         $logger->setPriority(Zend_Log::DEBUG);

        $as400 = new Payments_Service_As400();         $as400->addMapper($contactMapper, Contact_Model_Mapper_Contact::class)             ->addMapper($paymentsMapper, Invoice_Model_Mapper_Payments::class)             ->setPath(__DIR__ . DIRECTORY_SEPARATOR . '_files')             ->setLogger($logger);

        $as400->processBankPayments();         $this->assertCount(3, $logMock->events);         $this->assertEquals('Processing 401341345', $logMock->events[1]);         $this->assertEquals(             'Found contact "Foo Bar" for bank account BE93522511513933',             $logMock->events[2]         );     } }

104

Page 112: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Let’s focus on mocks first$contact = $this->getMockBuilder(Contact_Model_Contact::class)     ->setMethods(['getContactId', 'getName'])     ->getMock();

$contact->expects($this->any())     ->method('getContactId')     ->will($this->returnValue(1));

$contact->expects($this->any())     ->method('getName')     ->will($this->returnValue('Foo Bar'));

$contactMapper = $this->getMockBuilder(Contact_Model_Mapper_Contact::class)     ->setMethods(['findContactByBankAccount'])     ->getMock();

$contactMapper->expects($this->any())     ->method('findContactByBankAccount')     ->will($this->returnValue($contact));

$paymentsMapper = $this->getMockBuilder(Invoice_Model_Mapper_Payments::class)     ->setMethods(['updatePayment'])     ->getMock();

105

Page 113: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Write the log mock$logMock = new Zend_Log_Writer_Mock(); $logger = new Zend_Log(); $logger->setWriter($logMock); $logger->setPriority(Zend_Log::DEBUG);

$as400 = new Payments_Service_As400(); $as400->addMapper($contactMapper, Contact_Model_Mapper_Contact::class)     ->addMapper($paymentsMapper, Invoice_Model_Mapper_Payments::class)     ->setPath(__DIR__ . DIRECTORY_SEPARATOR . '_files')     ->setLogger($logger);

$as400->processBankPayments(); $this->assertCount(3, $logMock->events); $this->assertEquals('Processing 401341345', $logMock->events[1]); $this->assertEquals(     'Found contact "Foo Bar" for bank account BE93522511513933',     $logMock->events[2] );

106

Page 114: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development107

Page 115: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development108

ExerciseCheck out the web app

gitcheckoutlegacy-0.2

Go and check out branch legacy-0.2 where you will find the example test case.

10 minutes

Page 116: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Privates Exposed

109

http

://w

ww.

slas

hgea

r.com

/form

er-ts

a-ag

ent-a

dmits

-we-

knew

-full-

body

-sca

nner

s-di

dnt-w

ork-

3131

5288

/

Page 117: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Direct access forbidden?!?<?php

defined("_VALID_ACCESS") || die('Direct access forbidden');

/**  * This class provides dependency requirements  * @package epesi-base  * @subpackage module   */ class Dependency {

    private $module_name;     private $version_min;     private $version_max;     private $compare_max;

    private function __construct( $module_name, $version_min, $version_max, $version_max_is_ok = true) {         $this->module_name = $module_name;         $this->version_min = $version_min;         $this->version_max = $version_max;         $this->compare_max = $version_max_is_ok ? '<=' : '<';     }

    /** ... */ }

110

Page 118: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Don’t touch my junk!

111

http

s://w

ww.

flick

r.com

/pho

tos/

case

ymul

timed

ia/5

4122

9373

0

Page 119: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

House of Reflection

http

s://w

ww.

flick

r.com

/pho

tos/

tabo

r-roe

der/8

2507

7011

5

Page 120: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Let’s do this…<?php require_once 'include.php';

class DependencyTest extends PHPUnit_Framework_TestCase {     public function testConstructorSetsProperSettings()     {         require_once 'include/module_dependency.php';

        // We have a problem, the constructor is private!    } }

113

Page 121: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Using static method works too$params = array (     'moduleName' => 'Foo_Bar',     'minVersion' => 0,     'maxVersion' => 1,     'maxOk' => true, ); // We use a static method for this test $dependency = Dependency::requires_range(     $params['moduleName'],     $params['minVersion'],     $params['maxVersion'],     $params['maxOk'] );

// We use reflection to see if properties are set correctly $reflectionClass = new ReflectionClass('Dependency');

114

Page 122: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Asserting private properties// Let's retrieve the private properties $moduleName = $reflectionClass->getProperty('module_name'); $moduleName->setAccessible(true); $minVersion = $reflectionClass->getProperty('version_min'); $minVersion->setAccessible(true); $maxVersion = $reflectionClass->getProperty('version_max'); $maxVersion->setAccessible(true); $maxOk = $reflectionClass->getProperty('compare_max'); $maxOk->setAccessible(true);

// Let's assert $this->assertEquals($params['moduleName'], $moduleName->getValue($dependency),     'Expected value does not match the value set’); $this->assertEquals($params['minVersion'], $minVersion->getValue($dependency),     'Expected value does not match the value set’); $this->assertEquals($params['maxVersion'], $maxVersion->getValue($dependency),     'Expected value does not match the value set’); $this->assertEquals('<=', $maxOk->getValue($dependency),     'Expected value does not match the value set');

115

Page 123: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development116

Page 124: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Agenda

117

Introduction

TDD from scratch

TDD with legacy app

Additional tips

Recap

Page 125: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

PHPUnit requires basic PHP!Sometimes the challenge lies within PHP instead of direct PHPUnit

Testing is simple, coding is hard!

Testing is all about asserting that an actual process matches an expected result, so make sure you cover your expectations and test against those expectations

PHP functionality you need to know:

Reflection

Streams

System and PHP executions (e.g. “eval”, “passthru”, …)

118

Page 126: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

If you don’t know the destination…Start testing with what you know

Work your way up

119

Page 127: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

“But my code is too crappy…”

120

Page 128: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

For “untestable” codeWrite out the functionality in tests

Create a class providing this functionality (service, model, …)

Slowly move your existing code over to use the “cleaner” code

Bonus: you’ve got it already tested

121

Page 129: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Agenda

122

Introduction

TDD from scratch

TDD with legacy app

Additional tips

Recap

Page 130: Let your tests drive your code

Everything is testable!Not always easy, but always possible

Page 131: Let your tests drive your code

Write your tests firstWrite your code based on your tests

Page 132: Let your tests drive your code

Use code coverage as guideIt shows your progress through the code

Page 133: Let your tests drive your code

Be creative!Sometimes PHP can help out

Page 135: Let your tests drive your code

GitHub Repogithub.com/in2it-training/tdd-workshop

Page 136: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development

Thank you

129

http

s://w

ww.

flick

r.com

/pho

tos/

drew

m/3

1918

7251

5

Page 137: Let your tests drive your code

in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development130

in it2PROFESSIONAL PHP SERVICES

Michelangelo van DamZend Certified Engineer

[email protected] - www.in2it.be - T in2itvof - F in2itvof

Quality Assurance

Zend Framework 3Consulting

Disaster Recovery

Development Workflow

EnterprisePHP

TrainingMentoring

Our expertise services