SlideShare a Scribd company logo
1 of 137
Download to read offline
Let your tests drive your
development
An in2it workshop
in it2PROFESSIONAL PHP SERVICES
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
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
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
System’s Check
5
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Some conventions
PHPUnit 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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Functional requirement
Write a small PHP class with a method that will return the string “Hello
World!”
7
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
PHPUnit test
<?php
namespace AppTest;
use PHPUnitFrameworkTestCase; 

use AppHelloWorld;
class HelloWorldTest extends TestCase 

{
public function testAppOutputsHelloWorld()
{
$helloWorld = new HelloWorld();
$expectedAnswer = $helloWorld->sayHello();
$this->assertSame('Hello World!', $expectedAnswer);
}
}
8
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Running unit tests
9
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Writing the code
<?php
namespace App;
class HelloWorld 

{ 

public function sayHello(): string 

{ 

return 'Hello World!'; 

} 

}
10
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Re-run unit tests
11
Let your tests drive your code
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Change requests
13
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Change 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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Options
15
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Why change the existing test?
Code change so test has to change
New requirements change the test goal
16
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Why 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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
New test case
public function testAppOutputsHelloArgument() 

{ 

$helloWorld = new HelloWorld(); 

$expectedAnswer = $helloWorld->sayHello('unit testers'); 

$this->assertSame('Hello unit testers!', $expectedAnswer); 

}
18
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Running unit tests
19
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Re-run the tests
21
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
We introduced an error now!
22
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Finding the bug
<?php
namespace App;
class HelloWorld 

{ 

public function sayHello(string $arg): string 

{ 

return 'Hello ' . $arg . '!'; 

} 

}
23
No default value
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Fixing failure
<?php
namespace App;
class HelloWorld
{
public function sayHello(string $arg = 'World'): string
{
return 'Hello ' . $arg . '!';
}
}
24
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Re-run the tests
25
Let your tests drive your code
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Recap
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example
Get the source code
Go to the project folder and use the following commands
git	checkout	simple-example

./vendor/bin/phpunit	
All files will be there, so review them closely
28
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development29
Exercise
New 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
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
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Requirements as skeleton
<?php
namespace AppTestService;
use PHPUnitFrameworkTestCase;
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
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Example edge cases
public 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
Question
Why am I using very long and explicit method names for
my test methods?
Answer
To have human readable documentation about the features we’re developing and testing.
./vendor/bin/phpunit	--testdox
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
TestDox output
PHPUnit 6.1.3 by Sebastian Bergmann and contributors.
AppTestServiceTaskService
[ ] 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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development38
Exercise
Complete the test cases
git	checkout	tdd-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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
How we’re approaching this
We 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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Preparing our 1st test
40
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
class TaskServiceTest extends TestCase
{
    /* ... */
    protected function tearDown()
    {
        unset ($this->taskGateway);
    }
    /* ... */
}
42
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Creating gateway interface
<?php
namespace AppModel;
interface TaskGatewayInterface
{
    /**
     * Fetch all tasks from the back-end storage
     * @return Iterator
     */
    public function fetchAll(): Iterator;
}
44
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Creating entity interface
<?php
namespace AppModel;
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development46
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development47
Exercise
Check out the finished test cases
git	checkout	tdd-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
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Magic happening in setUp
Ideal 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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Project status
AppTestServiceTaskService
[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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development51
Exercise
Check out the finished test cases
git	checkout	tdd-ex3
Go and check out branch tdd-ex3 where you will
find the other completed test cases.
15 minutes
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development52
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
TextDox output
PHPUnit 6.1.3 by Sebastian Bergmann and contributors.
AppTestModelTaskEntity
[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
AppTestModelTaskGateway
[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
AppTestServiceTaskService
[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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development54
Exercise
Check out the web app
git	checkout	tdd-ex4	
php	-S	localhost:8000	-t	web	web/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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development55
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development56
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development57
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development58
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development59
Conclusion
Let’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.
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Legacy challenges
Not (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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Example project: EPESI
62
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development63
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Tests?!?
64
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Tests?!?
64
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
How to get started?
65
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Getting ready to test!
<?xml	version="1.0"	encoding="UTF-8"	?>	
<phpunit	
				bootstrap="vendor/autoload.php"	
				colors="true"	
				stopOnErrors="true"	
				stopOnFailures="true">	
				<testsuites>	
								<testsuite	name="App	unit	tests">	
												<directory	suffix="php">tests</directory>	
								</testsuite>	
				</testsuites>	
				<filter>	
								<whitelist>	
												<directory	suffix="php">src</directory>	
								</whitelist>	
				</filter>	
				<logging>	
								<log	type="coverage-html"	target="build/coverage"	lowUpperBound="35"	highLowerBound="70"/>	
				</logging>	
</phpunit>	
66
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Testing happily…
<?php
use PHPUnitFrameworkTestCase;
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development69
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Comment out the test
<?php
use PHPUnitFrameworkTestCase;
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
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
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
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
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
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
https://www.flickr.com/photos/marcgbx/7803086292
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development74
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development75
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development76
https://www.flickr.com/photos/christian_johannesen/2248244786
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development78
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
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]
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Add a “setUp” method
class ModuleManagerTest extends TestCase
{
    protected function setUp()
    {
        ModuleManager::$modules_install = [];
    }
    
    /* ... */
}
80
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development81
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development82
https://www.flickr.com/photos/evaekeblad/14780090550
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development84
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
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;
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development87
https://www.flickr.com/photos/sis/2497912343
Dead 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
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development90
https://www.flickr.com/photos/fragiletender/5332586299
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
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development93
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development94
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Completing all tests
95
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development97
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development98
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Recap
Testing 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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development100
Exercise
Check out these tests
git	checkout	legacy-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
What to do?
If your code doesn’t return values
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development103
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
class Payments_Service_As400Test extends PHPUnitFrameworkTestCase
{
    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
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
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development107
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development108
Exercise
Check out the web app
git	checkout	legacy-0.2
Go and check out branch legacy-0.2 where you
will find the example test case.
10 minutes
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Privates Exposed
109
http://www.slashgear.com/former-tsa-agent-admits-we-knew-full-body-scanners-didnt-work-31315288/
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Don’t touch my junk!
111
https://www.flickr.com/photos/caseymultimedia/5412293730
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
House of Reflection
https://www.flickr.com/photos/tabor-roeder/8250770115
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
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
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development116
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
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
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
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
“But my code is too crappy…”
120
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
For “untestable” code
Write 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
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
Everything is testable!
Not always easy, but always possible
Write your tests first
Write your code based on your tests
Use code coverage as guide
It shows your progress through the code
Be creative!
Sometimes PHP can help out
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Recommended reading
127
GitHub Repo
github.com/in2it-training/tdd-workshop
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Thank you
129
https://www.flickr.com/photos/drewm/3191872515
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development130
in it2PROFESSIONAL PHP SERVICES
Michelangelo van Dam
Zend Certified Engineer
contact@in2it.be - www.in2it.be - T in2itvof - F in2itvof
Quality
Assurance
Zend Framework 3
Consulting
Disaster
Recovery
Development
Workflow
Enterprise
PHP
Training
Mentoring
Our expertise services

More Related Content

What's hot

Multilanguage pipelines with Jenkins, Docker and Kubernetes (DevOpsDays Riga ...
Multilanguage pipelines with Jenkins, Docker and Kubernetes (DevOpsDays Riga ...Multilanguage pipelines with Jenkins, Docker and Kubernetes (DevOpsDays Riga ...
Multilanguage pipelines with Jenkins, Docker and Kubernetes (DevOpsDays Riga ...Jorge Hidalgo
 
Tracing Software Build Processes to Uncover License Compliance Inconsistencie...
Tracing Software Build Processes to Uncover License Compliance Inconsistencie...Tracing Software Build Processes to Uncover License Compliance Inconsistencie...
Tracing Software Build Processes to Uncover License Compliance Inconsistencie...Shane McIntosh
 
The Impact of Code Review Coverage and Participation on Software Quality
The Impact of Code Review Coverage and Participation on Software QualityThe Impact of Code Review Coverage and Participation on Software Quality
The Impact of Code Review Coverage and Participation on Software QualityShane McIntosh
 
Effective codereview | Dave Liddament | CODEiD
Effective codereview | Dave Liddament | CODEiDEffective codereview | Dave Liddament | CODEiD
Effective codereview | Dave Liddament | CODEiDCODEiD PHP Community
 
ITB2019 ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 V...
ITB2019 ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 V...ITB2019 ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 V...
ITB2019 ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 V...Ortus Solutions, Corp
 
Test-Driven Development for Embedded C -- OOP Conference 2015, Munich
Test-Driven Development for Embedded C -- OOP Conference 2015, MunichTest-Driven Development for Embedded C -- OOP Conference 2015, Munich
Test-Driven Development for Embedded C -- OOP Conference 2015, MunichJames Grenning
 
Mining Co-Change Information to Understand when Build Changes are Necessary
Mining Co-Change Information to Understand when Build Changes are NecessaryMining Co-Change Information to Understand when Build Changes are Necessary
Mining Co-Change Information to Understand when Build Changes are NecessaryShane McIntosh
 
Monitoring 改造計畫:流程觀點
Monitoring 改造計畫:流程觀點Monitoring 改造計畫:流程觀點
Monitoring 改造計畫:流程觀點William Yeh
 
Trying to build an Open Source browser in 2020
Trying to build an Open Source browser in 2020Trying to build an Open Source browser in 2020
Trying to build an Open Source browser in 2020Patricia Aas
 
DevSecOps for Developers, How To Start (ETC 2020)
DevSecOps for Developers, How To Start (ETC 2020)DevSecOps for Developers, How To Start (ETC 2020)
DevSecOps for Developers, How To Start (ETC 2020)Patricia Aas
 
Simple SQL Change Management with Sqitch
Simple SQL Change Management with SqitchSimple SQL Change Management with Sqitch
Simple SQL Change Management with SqitchDavid Wheeler
 
Sane SQL Change Management with Sqitch
Sane SQL Change Management with SqitchSane SQL Change Management with Sqitch
Sane SQL Change Management with SqitchDavid Wheeler
 
Trying to build an Open Source browser in 2020
Trying to build an Open Source browser in 2020Trying to build an Open Source browser in 2020
Trying to build an Open Source browser in 2020Patricia Aas
 
Continuous Delivery and Automated Operations on k8s with keptn
Continuous Delivery and Automated Operations on k8s with keptnContinuous Delivery and Automated Operations on k8s with keptn
Continuous Delivery and Automated Operations on k8s with keptnAndreas Grabner
 
Designing process flows with #Baker
Designing process flows with #BakerDesigning process flows with #Baker
Designing process flows with #BakerBekir Oguz
 
Identifying Hotspots in the PostgreSQL Build Process
Identifying Hotspots in the PostgreSQL Build ProcessIdentifying Hotspots in the PostgreSQL Build Process
Identifying Hotspots in the PostgreSQL Build ProcessShane McIntosh
 
Continuous Integration for Spark Apps by Sean McIntyre
Continuous Integration for Spark Apps by Sean McIntyreContinuous Integration for Spark Apps by Sean McIntyre
Continuous Integration for Spark Apps by Sean McIntyreSpark Summit
 

What's hot (20)

Multilanguage pipelines with Jenkins, Docker and Kubernetes (DevOpsDays Riga ...
Multilanguage pipelines with Jenkins, Docker and Kubernetes (DevOpsDays Riga ...Multilanguage pipelines with Jenkins, Docker and Kubernetes (DevOpsDays Riga ...
Multilanguage pipelines with Jenkins, Docker and Kubernetes (DevOpsDays Riga ...
 
Tracing Software Build Processes to Uncover License Compliance Inconsistencie...
Tracing Software Build Processes to Uncover License Compliance Inconsistencie...Tracing Software Build Processes to Uncover License Compliance Inconsistencie...
Tracing Software Build Processes to Uncover License Compliance Inconsistencie...
 
The Impact of Code Review Coverage and Participation on Software Quality
The Impact of Code Review Coverage and Participation on Software QualityThe Impact of Code Review Coverage and Participation on Software Quality
The Impact of Code Review Coverage and Participation on Software Quality
 
Effective codereview | Dave Liddament | CODEiD
Effective codereview | Dave Liddament | CODEiDEffective codereview | Dave Liddament | CODEiD
Effective codereview | Dave Liddament | CODEiD
 
CDI 2.0 is upon us Devoxx
CDI 2.0 is upon us DevoxxCDI 2.0 is upon us Devoxx
CDI 2.0 is upon us Devoxx
 
ITB2019 ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 V...
ITB2019 ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 V...ITB2019 ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 V...
ITB2019 ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 V...
 
Test-Driven Development for Embedded C -- OOP Conference 2015, Munich
Test-Driven Development for Embedded C -- OOP Conference 2015, MunichTest-Driven Development for Embedded C -- OOP Conference 2015, Munich
Test-Driven Development for Embedded C -- OOP Conference 2015, Munich
 
Mining Co-Change Information to Understand when Build Changes are Necessary
Mining Co-Change Information to Understand when Build Changes are NecessaryMining Co-Change Information to Understand when Build Changes are Necessary
Mining Co-Change Information to Understand when Build Changes are Necessary
 
Monitoring 改造計畫:流程觀點
Monitoring 改造計畫:流程觀點Monitoring 改造計畫:流程觀點
Monitoring 改造計畫:流程觀點
 
Trying to build an Open Source browser in 2020
Trying to build an Open Source browser in 2020Trying to build an Open Source browser in 2020
Trying to build an Open Source browser in 2020
 
DevSecOps for Developers, How To Start (ETC 2020)
DevSecOps for Developers, How To Start (ETC 2020)DevSecOps for Developers, How To Start (ETC 2020)
DevSecOps for Developers, How To Start (ETC 2020)
 
CDI In Real Life
CDI In Real LifeCDI In Real Life
CDI In Real Life
 
Simple SQL Change Management with Sqitch
Simple SQL Change Management with SqitchSimple SQL Change Management with Sqitch
Simple SQL Change Management with Sqitch
 
Sane SQL Change Management with Sqitch
Sane SQL Change Management with SqitchSane SQL Change Management with Sqitch
Sane SQL Change Management with Sqitch
 
Trying to build an Open Source browser in 2020
Trying to build an Open Source browser in 2020Trying to build an Open Source browser in 2020
Trying to build an Open Source browser in 2020
 
Continuous Delivery and Automated Operations on k8s with keptn
Continuous Delivery and Automated Operations on k8s with keptnContinuous Delivery and Automated Operations on k8s with keptn
Continuous Delivery and Automated Operations on k8s with keptn
 
Designing process flows with #Baker
Designing process flows with #BakerDesigning process flows with #Baker
Designing process flows with #Baker
 
Identifying Hotspots in the PostgreSQL Build Process
Identifying Hotspots in the PostgreSQL Build ProcessIdentifying Hotspots in the PostgreSQL Build Process
Identifying Hotspots in the PostgreSQL Build Process
 
Continuous Integration for Spark Apps by Sean McIntyre
Continuous Integration for Spark Apps by Sean McIntyreContinuous Integration for Spark Apps by Sean McIntyre
Continuous Integration for Spark Apps by Sean McIntyre
 
How Testing Changed My Life
How Testing Changed My LifeHow Testing Changed My Life
How Testing Changed My Life
 

Similar to Let your tests drive your code

Behavioural Driven Development in Zf2
Behavioural Driven Development in Zf2Behavioural Driven Development in Zf2
Behavioural Driven Development in Zf2David Contavalli
 
DevOps Interview Questions and Answers 2019 | DevOps Tutorial | Edureka
DevOps Interview Questions and Answers 2019 | DevOps Tutorial | EdurekaDevOps Interview Questions and Answers 2019 | DevOps Tutorial | Edureka
DevOps Interview Questions and Answers 2019 | DevOps Tutorial | EdurekaEdureka!
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven DevelopmentPapp Laszlo
 
Technologist_Interviews___Welcome_to_Modanisa_Wiki_2 (1).pdf
Technologist_Interviews___Welcome_to_Modanisa_Wiki_2 (1).pdfTechnologist_Interviews___Welcome_to_Modanisa_Wiki_2 (1).pdf
Technologist_Interviews___Welcome_to_Modanisa_Wiki_2 (1).pdfzohaib247331
 
Test Driven iOS Development (TDD)
Test Driven iOS Development (TDD)Test Driven iOS Development (TDD)
Test Driven iOS Development (TDD)Babul Mirdha
 
Tdd is not about testing (C++ version)
Tdd is not about testing (C++ version)Tdd is not about testing (C++ version)
Tdd is not about testing (C++ version)Gianluca Padovani
 
High Productivity Web Development Workflow
High Productivity Web Development WorkflowHigh Productivity Web Development Workflow
High Productivity Web Development WorkflowVũ Nguyễn
 
High productivity web development workflow - JavaScript Meetup Saigon 2014
High productivity web development workflow - JavaScript Meetup Saigon 2014High productivity web development workflow - JavaScript Meetup Saigon 2014
High productivity web development workflow - JavaScript Meetup Saigon 2014Oliver N
 
Tdd is not about testing (OOP)
Tdd is not about testing (OOP)Tdd is not about testing (OOP)
Tdd is not about testing (OOP)Gianluca Padovani
 
Behavior Driven Development by Example
Behavior Driven Development by ExampleBehavior Driven Development by Example
Behavior Driven Development by ExampleNalin Goonawardana
 
Test driven development_continuous_integration
Test driven development_continuous_integrationTest driven development_continuous_integration
Test driven development_continuous_integrationhaochenglee
 
GlobalLogic Test Automation Online TechTalk “Test Driven Development as a Per...
GlobalLogic Test Automation Online TechTalk “Test Driven Development as a Per...GlobalLogic Test Automation Online TechTalk “Test Driven Development as a Per...
GlobalLogic Test Automation Online TechTalk “Test Driven Development as a Per...GlobalLogic Ukraine
 
Attacking Pipelines--Security meets Continuous Delivery
Attacking Pipelines--Security meets Continuous DeliveryAttacking Pipelines--Security meets Continuous Delivery
Attacking Pipelines--Security meets Continuous DeliveryJames Wickett
 
Codeception introduction and use in Yii
Codeception introduction and use in YiiCodeception introduction and use in Yii
Codeception introduction and use in YiiIlPeach
 
What is the best approach to tdd
What is the best approach to tddWhat is the best approach to tdd
What is the best approach to tddLuca Mattia Ferrari
 
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...mCloud
 
Probo.ci Drupal 4 Gov Devops 1/2 day Presentation
Probo.ci Drupal 4 Gov Devops 1/2 day Presentation Probo.ci Drupal 4 Gov Devops 1/2 day Presentation
Probo.ci Drupal 4 Gov Devops 1/2 day Presentation Zivtech, LLC
 
Refactoring ASP.NET and beyond
Refactoring ASP.NET and beyondRefactoring ASP.NET and beyond
Refactoring ASP.NET and beyondDotNetMarche
 

Similar to Let your tests drive your code (20)

Behavioural Driven Development in Zf2
Behavioural Driven Development in Zf2Behavioural Driven Development in Zf2
Behavioural Driven Development in Zf2
 
DevOps Interview Questions and Answers 2019 | DevOps Tutorial | Edureka
DevOps Interview Questions and Answers 2019 | DevOps Tutorial | EdurekaDevOps Interview Questions and Answers 2019 | DevOps Tutorial | Edureka
DevOps Interview Questions and Answers 2019 | DevOps Tutorial | Edureka
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
 
Technologist_Interviews___Welcome_to_Modanisa_Wiki_2 (1).pdf
Technologist_Interviews___Welcome_to_Modanisa_Wiki_2 (1).pdfTechnologist_Interviews___Welcome_to_Modanisa_Wiki_2 (1).pdf
Technologist_Interviews___Welcome_to_Modanisa_Wiki_2 (1).pdf
 
Test Driven iOS Development (TDD)
Test Driven iOS Development (TDD)Test Driven iOS Development (TDD)
Test Driven iOS Development (TDD)
 
Android NDK: Entrando no Mundo Nativo
Android NDK: Entrando no Mundo NativoAndroid NDK: Entrando no Mundo Nativo
Android NDK: Entrando no Mundo Nativo
 
Tdd is not about testing (C++ version)
Tdd is not about testing (C++ version)Tdd is not about testing (C++ version)
Tdd is not about testing (C++ version)
 
High Productivity Web Development Workflow
High Productivity Web Development WorkflowHigh Productivity Web Development Workflow
High Productivity Web Development Workflow
 
High productivity web development workflow - JavaScript Meetup Saigon 2014
High productivity web development workflow - JavaScript Meetup Saigon 2014High productivity web development workflow - JavaScript Meetup Saigon 2014
High productivity web development workflow - JavaScript Meetup Saigon 2014
 
Tdd is not about testing (OOP)
Tdd is not about testing (OOP)Tdd is not about testing (OOP)
Tdd is not about testing (OOP)
 
Coding Naked 2023
Coding Naked 2023Coding Naked 2023
Coding Naked 2023
 
Behavior Driven Development by Example
Behavior Driven Development by ExampleBehavior Driven Development by Example
Behavior Driven Development by Example
 
Test driven development_continuous_integration
Test driven development_continuous_integrationTest driven development_continuous_integration
Test driven development_continuous_integration
 
GlobalLogic Test Automation Online TechTalk “Test Driven Development as a Per...
GlobalLogic Test Automation Online TechTalk “Test Driven Development as a Per...GlobalLogic Test Automation Online TechTalk “Test Driven Development as a Per...
GlobalLogic Test Automation Online TechTalk “Test Driven Development as a Per...
 
Attacking Pipelines--Security meets Continuous Delivery
Attacking Pipelines--Security meets Continuous DeliveryAttacking Pipelines--Security meets Continuous Delivery
Attacking Pipelines--Security meets Continuous Delivery
 
Codeception introduction and use in Yii
Codeception introduction and use in YiiCodeception introduction and use in Yii
Codeception introduction and use in Yii
 
What is the best approach to tdd
What is the best approach to tddWhat is the best approach to tdd
What is the best approach to tdd
 
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
 
Probo.ci Drupal 4 Gov Devops 1/2 day Presentation
Probo.ci Drupal 4 Gov Devops 1/2 day Presentation Probo.ci Drupal 4 Gov Devops 1/2 day Presentation
Probo.ci Drupal 4 Gov Devops 1/2 day Presentation
 
Refactoring ASP.NET and beyond
Refactoring ASP.NET and beyondRefactoring ASP.NET and beyond
Refactoring ASP.NET and beyond
 

More from Michelangelo van Dam

GDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and defaultGDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and defaultMichelangelo van Dam
 
Moving from app services to azure functions
Moving from app services to azure functionsMoving from app services to azure functions
Moving from app services to azure functionsMichelangelo van Dam
 
General Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's storyGeneral Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's storyMichelangelo van Dam
 
Leveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantageLeveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantageMichelangelo van Dam
 
Open source for a successful business
Open source for a successful businessOpen source for a successful business
Open source for a successful businessMichelangelo van Dam
 
Decouple your framework now, thank me later
Decouple your framework now, thank me laterDecouple your framework now, thank me later
Decouple your framework now, thank me laterMichelangelo van Dam
 
Deploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutesDeploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutesMichelangelo van Dam
 
Azure and OSS, a match made in heaven
Azure and OSS, a match made in heavenAzure and OSS, a match made in heaven
Azure and OSS, a match made in heavenMichelangelo van Dam
 
Zf2 how arrays will save your project
Zf2   how arrays will save your projectZf2   how arrays will save your project
Zf2 how arrays will save your projectMichelangelo van Dam
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsMichelangelo van Dam
 
Easily extend your existing php app with an api
Easily extend your existing php app with an apiEasily extend your existing php app with an api
Easily extend your existing php app with an apiMichelangelo van Dam
 

More from Michelangelo van Dam (20)

GDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and defaultGDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and default
 
Moving from app services to azure functions
Moving from app services to azure functionsMoving from app services to azure functions
Moving from app services to azure functions
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
 
DevOps or DevSecOps
DevOps or DevSecOpsDevOps or DevSecOps
DevOps or DevSecOps
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
 
General Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's storyGeneral Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's story
 
Leveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantageLeveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantage
 
The road to php 7.1
The road to php 7.1The road to php 7.1
The road to php 7.1
 
Open source for a successful business
Open source for a successful businessOpen source for a successful business
Open source for a successful business
 
Decouple your framework now, thank me later
Decouple your framework now, thank me laterDecouple your framework now, thank me later
Decouple your framework now, thank me later
 
Deploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutesDeploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutes
 
Azure and OSS, a match made in heaven
Azure and OSS, a match made in heavenAzure and OSS, a match made in heaven
Azure and OSS, a match made in heaven
 
Getting hands dirty with php7
Getting hands dirty with php7Getting hands dirty with php7
Getting hands dirty with php7
 
Zf2 how arrays will save your project
Zf2   how arrays will save your projectZf2   how arrays will save your project
Zf2 how arrays will save your project
 
Create, test, secure, repeat
Create, test, secure, repeatCreate, test, secure, repeat
Create, test, secure, repeat
 
The Continuous PHP Pipeline
The Continuous PHP PipelineThe Continuous PHP Pipeline
The Continuous PHP Pipeline
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the tests
 
Easily extend your existing php app with an api
Easily extend your existing php app with an apiEasily extend your existing php app with an api
Easily extend your existing php app with an api
 
Your code are my tests
Your code are my testsYour code are my tests
Your code are my tests
 
200K+ reasons security is a must
200K+ reasons security is a must200K+ reasons security is a must
200K+ reasons security is a must
 

Recently uploaded

Popular-NO1 Kala Jadu Expert Specialist In Germany Kala Jadu Expert Specialis...
Popular-NO1 Kala Jadu Expert Specialist In Germany Kala Jadu Expert Specialis...Popular-NO1 Kala Jadu Expert Specialist In Germany Kala Jadu Expert Specialis...
Popular-NO1 Kala Jadu Expert Specialist In Germany Kala Jadu Expert Specialis...Amil baba
 
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdf
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdfRenewable Energy & Entrepreneurship Workshop_21Feb2024.pdf
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdfodunowoeminence2019
 
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....santhyamuthu1
 
Strategies of Urban Morphologyfor Improving Outdoor Thermal Comfort and Susta...
Strategies of Urban Morphologyfor Improving Outdoor Thermal Comfort and Susta...Strategies of Urban Morphologyfor Improving Outdoor Thermal Comfort and Susta...
Strategies of Urban Morphologyfor Improving Outdoor Thermal Comfort and Susta...amrabdallah9
 
Guardians and Glitches: Navigating the Duality of Gen AI in AppSec
Guardians and Glitches: Navigating the Duality of Gen AI in AppSecGuardians and Glitches: Navigating the Duality of Gen AI in AppSec
Guardians and Glitches: Navigating the Duality of Gen AI in AppSecTrupti Shiralkar, CISSP
 
ChatGPT-and-Generative-AI-Landscape Working of generative ai search
ChatGPT-and-Generative-AI-Landscape Working of generative ai searchChatGPT-and-Generative-AI-Landscape Working of generative ai search
ChatGPT-and-Generative-AI-Landscape Working of generative ai searchrohitcse52
 
ASME BPVC 2023 Section I para leer y entender
ASME BPVC 2023 Section I para leer y entenderASME BPVC 2023 Section I para leer y entender
ASME BPVC 2023 Section I para leer y entenderjuancarlos286641
 
IT3401-WEB ESSENTIALS PRESENTATIONS.pptx
IT3401-WEB ESSENTIALS PRESENTATIONS.pptxIT3401-WEB ESSENTIALS PRESENTATIONS.pptx
IT3401-WEB ESSENTIALS PRESENTATIONS.pptxSAJITHABANUS
 
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...Sean Meyn
 
How to Write a Good Scientific Paper.pdf
How to Write a Good Scientific Paper.pdfHow to Write a Good Scientific Paper.pdf
How to Write a Good Scientific Paper.pdfRedhwan Qasem Shaddad
 
Nodal seismic construction requirements.pptx
Nodal seismic construction requirements.pptxNodal seismic construction requirements.pptx
Nodal seismic construction requirements.pptxwendy cai
 
Phase noise transfer functions.pptx
Phase noise transfer      functions.pptxPhase noise transfer      functions.pptx
Phase noise transfer functions.pptxSaiGouthamSunkara
 
Landsman converter for power factor improvement
Landsman converter for power factor improvementLandsman converter for power factor improvement
Landsman converter for power factor improvementVijayMuni2
 
cloud computing notes for anna university syllabus
cloud computing notes for anna university syllabuscloud computing notes for anna university syllabus
cloud computing notes for anna university syllabusViolet Violet
 

Recently uploaded (20)

Popular-NO1 Kala Jadu Expert Specialist In Germany Kala Jadu Expert Specialis...
Popular-NO1 Kala Jadu Expert Specialist In Germany Kala Jadu Expert Specialis...Popular-NO1 Kala Jadu Expert Specialist In Germany Kala Jadu Expert Specialis...
Popular-NO1 Kala Jadu Expert Specialist In Germany Kala Jadu Expert Specialis...
 
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdf
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdfRenewable Energy & Entrepreneurship Workshop_21Feb2024.pdf
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdf
 
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....
 
Strategies of Urban Morphologyfor Improving Outdoor Thermal Comfort and Susta...
Strategies of Urban Morphologyfor Improving Outdoor Thermal Comfort and Susta...Strategies of Urban Morphologyfor Improving Outdoor Thermal Comfort and Susta...
Strategies of Urban Morphologyfor Improving Outdoor Thermal Comfort and Susta...
 
Guardians and Glitches: Navigating the Duality of Gen AI in AppSec
Guardians and Glitches: Navigating the Duality of Gen AI in AppSecGuardians and Glitches: Navigating the Duality of Gen AI in AppSec
Guardians and Glitches: Navigating the Duality of Gen AI in AppSec
 
Lecture 2 .pptx
Lecture 2                            .pptxLecture 2                            .pptx
Lecture 2 .pptx
 
Présentation IIRB 2024 Chloe Dufrane.pdf
Présentation IIRB 2024 Chloe Dufrane.pdfPrésentation IIRB 2024 Chloe Dufrane.pdf
Présentation IIRB 2024 Chloe Dufrane.pdf
 
ChatGPT-and-Generative-AI-Landscape Working of generative ai search
ChatGPT-and-Generative-AI-Landscape Working of generative ai searchChatGPT-and-Generative-AI-Landscape Working of generative ai search
ChatGPT-and-Generative-AI-Landscape Working of generative ai search
 
ASME BPVC 2023 Section I para leer y entender
ASME BPVC 2023 Section I para leer y entenderASME BPVC 2023 Section I para leer y entender
ASME BPVC 2023 Section I para leer y entender
 
IT3401-WEB ESSENTIALS PRESENTATIONS.pptx
IT3401-WEB ESSENTIALS PRESENTATIONS.pptxIT3401-WEB ESSENTIALS PRESENTATIONS.pptx
IT3401-WEB ESSENTIALS PRESENTATIONS.pptx
 
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...
 
Lecture 2 .pdf
Lecture 2                           .pdfLecture 2                           .pdf
Lecture 2 .pdf
 
How to Write a Good Scientific Paper.pdf
How to Write a Good Scientific Paper.pdfHow to Write a Good Scientific Paper.pdf
How to Write a Good Scientific Paper.pdf
 
Nodal seismic construction requirements.pptx
Nodal seismic construction requirements.pptxNodal seismic construction requirements.pptx
Nodal seismic construction requirements.pptx
 
Phase noise transfer functions.pptx
Phase noise transfer      functions.pptxPhase noise transfer      functions.pptx
Phase noise transfer functions.pptx
 
Litature Review: Research Paper work for Engineering
Litature Review: Research Paper work for EngineeringLitature Review: Research Paper work for Engineering
Litature Review: Research Paper work for Engineering
 
Landsman converter for power factor improvement
Landsman converter for power factor improvementLandsman converter for power factor improvement
Landsman converter for power factor improvement
 
cloud computing notes for anna university syllabus
cloud computing notes for anna university syllabuscloud computing notes for anna university syllabus
cloud computing notes for anna university syllabus
 
Lecture 4 .pdf
Lecture 4                              .pdfLecture 4                              .pdf
Lecture 4 .pdf
 
計劃趕得上變化
計劃趕得上變化計劃趕得上變化
計劃趕得上變化
 

Let your tests drive your code

  • 1. Let your tests drive your development An in2it workshop in it2PROFESSIONAL PHP SERVICES
  • 2. 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
  • 3. 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
  • 4. 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
  • 5. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development System’s Check 5
  • 6. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Some conventions PHPUnit 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
  • 7. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Functional requirement Write a small PHP class with a method that will return the string “Hello World!” 7
  • 8. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example PHPUnit test <?php namespace AppTest; use PHPUnitFrameworkTestCase; 
 use AppHelloWorld; class HelloWorldTest extends TestCase 
 { public function testAppOutputsHelloWorld() { $helloWorld = new HelloWorld(); $expectedAnswer = $helloWorld->sayHello(); $this->assertSame('Hello World!', $expectedAnswer); } } 8
  • 9. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Running unit tests 9
  • 10. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Writing the code <?php namespace App; class HelloWorld 
 { 
 public function sayHello(): string 
 { 
 return 'Hello World!'; 
 } 
 } 10
  • 11. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Re-run unit tests 11
  • 13. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Change requests 13
  • 14. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Change 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
  • 15. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Options 15
  • 16. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Why change the existing test? Code change so test has to change New requirements change the test goal 16
  • 17. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Why 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
  • 18. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example New test case public function testAppOutputsHelloArgument() 
 { 
 $helloWorld = new HelloWorld(); 
 $expectedAnswer = $helloWorld->sayHello('unit testers'); 
 $this->assertSame('Hello unit testers!', $expectedAnswer); 
 } 18
  • 19. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Running unit tests 19
  • 20. 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
  • 21. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Re-run the tests 21
  • 22. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development We introduced an error now! 22
  • 23. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Finding the bug <?php namespace App; class HelloWorld 
 { 
 public function sayHello(string $arg): string 
 { 
 return 'Hello ' . $arg . '!'; 
 } 
 } 23 No default value
  • 24. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Fixing failure <?php namespace App; class HelloWorld { public function sayHello(string $arg = 'World'): string { return 'Hello ' . $arg . '!'; } } 24
  • 25. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Re-run the tests 25
  • 27. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Recap 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
  • 28. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Simple example Get the source code Go to the project folder and use the following commands git checkout simple-example
 ./vendor/bin/phpunit All files will be there, so review them closely 28
  • 29. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development29 Exercise New 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
  • 30. 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
  • 31. 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
  • 32. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Requirements as skeleton <?php namespace AppTestService; use PHPUnitFrameworkTestCase; 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
  • 33. 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
  • 34. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Example edge cases public 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
  • 35. Question Why am I using very long and explicit method names for my test methods?
  • 36. Answer To have human readable documentation about the features we’re developing and testing. ./vendor/bin/phpunit --testdox
  • 37. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development TestDox output PHPUnit 6.1.3 by Sebastian Bergmann and contributors. AppTestServiceTaskService [ ] 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
  • 38. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development38 Exercise Complete the test cases git checkout tdd-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
  • 39. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development How we’re approaching this We 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
  • 40. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Preparing our 1st test 40
  • 41. 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
  • 42. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development class TaskServiceTest extends TestCase {     /* ... */     protected function tearDown()     {         unset ($this->taskGateway);     }     /* ... */ } 42
  • 43. 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
  • 44. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Creating gateway interface <?php namespace AppModel; interface TaskGatewayInterface {     /**      * Fetch all tasks from the back-end storage      * @return Iterator      */     public function fetchAll(): Iterator; } 44
  • 45. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Creating entity interface <?php namespace AppModel; 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
  • 46. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development46
  • 47. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development47 Exercise Check out the finished test cases git checkout tdd-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
  • 48. 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
  • 49. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Magic happening in setUp Ideal 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
  • 50. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Project status AppTestServiceTaskService [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
  • 51. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development51 Exercise Check out the finished test cases git checkout tdd-ex3 Go and check out branch tdd-ex3 where you will find the other completed test cases. 15 minutes
  • 52. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development52
  • 53. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development TextDox output PHPUnit 6.1.3 by Sebastian Bergmann and contributors. AppTestModelTaskEntity [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 AppTestModelTaskGateway [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 AppTestServiceTaskService [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
  • 54. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development54 Exercise Check out the web app git checkout tdd-ex4 php -S localhost:8000 -t web web/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
  • 55. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development55
  • 56. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development56
  • 57. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development57
  • 58. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development58
  • 59. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development59 Conclusion Let’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.
  • 60. 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
  • 61. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Legacy challenges Not (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
  • 62. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Example project: EPESI 62
  • 63. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development63
  • 64. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Tests?!? 64
  • 65. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Tests?!? 64
  • 66. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development How to get started? 65
  • 67. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Getting ready to test! <?xml version="1.0" encoding="UTF-8" ?> <phpunit bootstrap="vendor/autoload.php" colors="true" stopOnErrors="true" stopOnFailures="true"> <testsuites> <testsuite name="App unit tests"> <directory suffix="php">tests</directory> </testsuite> </testsuites> <filter> <whitelist> <directory suffix="php">src</directory> </whitelist> </filter> <logging> <log type="coverage-html" target="build/coverage" lowUpperBound="35" highLowerBound="70"/> </logging> </phpunit> 66
  • 68. 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
  • 69. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Testing happily… <?php use PHPUnitFrameworkTestCase; 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
  • 70. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development69
  • 71. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Comment out the test <?php use PHPUnitFrameworkTestCase; 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
  • 72. 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
  • 73. 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
  • 74. 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
  • 75. 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
  • 76. 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
  • 78. 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
  • 79. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development74
  • 80. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development75
  • 81. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development76 https://www.flickr.com/photos/christian_johannesen/2248244786
  • 82. 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
  • 83. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development78
  • 84. 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
  • 85. 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]
  • 86. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Add a “setUp” method class ModuleManagerTest extends TestCase {     protected function setUp()     {         ModuleManager::$modules_install = [];     }          /* ... */ } 80
  • 87. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development81
  • 88. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development82 https://www.flickr.com/photos/evaekeblad/14780090550
  • 89. 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
  • 90. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development84
  • 91. 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
  • 92. 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;
  • 93. 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
  • 94. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development87 https://www.flickr.com/photos/sis/2497912343 Dead Code
  • 95. 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
  • 96. 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
  • 97. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development90 https://www.flickr.com/photos/fragiletender/5332586299
  • 98. 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
  • 99. 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
  • 100. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development93
  • 101. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development94
  • 102. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Completing all tests 95
  • 103. 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
  • 104. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development97
  • 105. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development98
  • 106. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Recap Testing 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
  • 107. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development100 Exercise Check out these tests git checkout legacy-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
  • 108. What to do? If your code doesn’t return values
  • 109. 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
  • 110. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development103
  • 111. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development class Payments_Service_As400Test extends PHPUnitFrameworkTestCase {     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
  • 112. 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
  • 113. 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
  • 114. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development107
  • 115. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development108 Exercise Check out the web app git checkout legacy-0.2 Go and check out branch legacy-0.2 where you will find the example test case. 10 minutes
  • 116. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Privates Exposed 109 http://www.slashgear.com/former-tsa-agent-admits-we-knew-full-body-scanners-didnt-work-31315288/
  • 117. 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
  • 118. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Don’t touch my junk! 111 https://www.flickr.com/photos/caseymultimedia/5412293730
  • 119. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development House of Reflection https://www.flickr.com/photos/tabor-roeder/8250770115
  • 120. 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
  • 121. 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
  • 122. 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
  • 123. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development116
  • 124. 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
  • 125. 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
  • 126. 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
  • 127. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development “But my code is too crappy…” 120
  • 128. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development For “untestable” code Write 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
  • 129. 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
  • 130. Everything is testable! Not always easy, but always possible
  • 131. Write your tests first Write your code based on your tests
  • 132. Use code coverage as guide It shows your progress through the code
  • 133. Be creative! Sometimes PHP can help out
  • 134. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Recommended reading 127
  • 136. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development Thank you 129 https://www.flickr.com/photos/drewm/3191872515
  • 137. in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development130 in it2PROFESSIONAL PHP SERVICES Michelangelo van Dam Zend Certified Engineer contact@in2it.be - www.in2it.be - T in2itvof - F in2itvof Quality Assurance Zend Framework 3 Consulting Disaster Recovery Development Workflow Enterprise PHP Training Mentoring Our expertise services