let your tests drive your code
TRANSCRIPT
![Page 1: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/1.jpg)
Let your tests drive your development
An in2it workshop
in it2PROFESSIONAL PHP SERVICES
![Page 2: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/2.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Agenda
2
Introduction
TDD from scratch
TDD with legacy app
Additional tips
Recap
![Page 3: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/3.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Agenda
3
Introduction
TDD from scratch
TDD with legacy app
Additional tips
Recap
![Page 4: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/4.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
What is test-driven development (TDD)?Write unit tests first
They will fail
Write functional code in accordance of the tests
Your tests will structure the way you write your code
Re-run your tests again
They should pass
4
![Page 5: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/5.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
System’s Check5
![Page 6: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/6.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Some conventionsPHPUnit was installed using composer
All vendor packages were installed with the code base
Running PHPUnit with ./vendor/bin/phpunit
If you use a different approach, make sure it works for you
GIT is used for the exercises, make sure you know about
checking out branches
reading git logs
In the slides I left out comments to save space
6
![Page 7: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/7.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleFunctional requirement
Write a small PHP class with a method that will return the string “Hello World!”
7
![Page 8: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/8.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple examplePHPUnit test
<?php
namespace App\Test;
use PHPUnit\Framework\TestCase; use App\HelloWorld;
class HelloWorldTest extends TestCase {
public function testAppOutputsHelloWorld() { $helloWorld = new HelloWorld(); $expectedAnswer = $helloWorld->sayHello(); $this->assertSame('Hello World!', $expectedAnswer); } }
8
![Page 9: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/9.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleRunning unit tests
9
![Page 10: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/10.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleWriting the code
<?php
namespace App;
class HelloWorld { public function sayHello(): string { return 'Hello World!'; } }
10
![Page 11: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/11.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleRe-run unit tests
11
![Page 12: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/12.jpg)
![Page 13: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/13.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Change requests
13
![Page 14: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/14.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleChange request
Update our small PHP class method that will allow an argument and will return the string “Hello <arg>!” where <arg> is the argument provided.
14
![Page 15: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/15.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleOptions
15
![Page 16: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/16.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleWhy change the existing test?
Code change so test has to change
New requirements change the test goal
16
![Page 17: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/17.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleWhy not change the existing test?
Code requirement change so new test is required
We don’t want to change existing requirements
Prevent BC breaks
New test will cover changing requirements
17
![Page 18: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/18.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleNew test case
public function testAppOutputsHelloArgument() { $helloWorld = new HelloWorld(); $expectedAnswer = $helloWorld->sayHello('unit testers'); $this->assertSame('Hello unit testers!', $expectedAnswer); }
18
![Page 19: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/19.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleRunning unit tests
19
![Page 20: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/20.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple example Changing the code
<?php
namespace App;
class HelloWorld { public function sayHello(string $arg): string { return 'Hello ' . $arg . '!'; } }
20
![Page 21: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/21.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleRe-run the tests
21
![Page 22: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/22.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
We introduced an error now!
22
![Page 23: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/23.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleFinding the bug
<?php
namespace App;
class HelloWorld { public function sayHello(string $arg): string { return 'Hello ' . $arg . '!'; } }
23
No default value
![Page 24: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/24.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleFixing failure
<?php
namespace App;
class HelloWorld { public function sayHello(string $arg = 'World'): string { return 'Hello ' . $arg . '!'; } }
24
![Page 25: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/25.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleRe-run the tests
25
![Page 26: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/26.jpg)
![Page 27: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/27.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleRecap
Write test based on functionality (and run test)
Write code based on functionality (and re-run test)
Write new test based on changed functionality (and re-run tests)
Change code based on functionality (and re-run tests)
Update code until all tests are passing
27
![Page 28: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/28.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Simple exampleGet the source code
Go to the project folder and use the following commands gitcheckoutsimple-example./vendor/bin/phpunit
All files will be there, so review them closely
28
![Page 29: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/29.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development29
ExerciseNew change requirements
Security is important, so we need to validate the given argument so it only accepts string type values. If something other than a string is provided, an exception should be raised.
10 minutes
![Page 30: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/30.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Agenda
30
Introduction
TDD from scratch
TDD with legacy app
Additional tips
Recap
![Page 31: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/31.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Project “TodoToDone”Project “TodoToDone” is a simple todo tool, tracking the tasks that you need to do. It should provide the following features:
List open tasks sorted newest to oldest
Create a new task (label and description)
Update an existing task
Mark task as done in the overview list
Remove task marked as done
31
![Page 32: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/32.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Requirements as skeleton<?php
namespace App\Test\Service;
use PHPUnit\Framework\TestCase;
class TaskServiceTest extends TestCase { public function testServiceReturnsListOfTasks() { // List open tasks sorted newest to oldest }
public function testServiceCanAddNewTask() { // Create a new task (label and description) }
public function testServiceCanUpdateExistingTask() { // Update an existing task }
public function testServiceCanMarkTaskAsDone() { // Mark task as done in the overview list }
public function testServiceCanRemoveTaskMarkedAsDone() { // Remove task marked as done } }
32
![Page 33: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/33.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Don’t start writing test yet!Unit testing is about looking at a specific task from every angle
Define use and edge cases and add them as additional tests
33
![Page 34: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/34.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Example edge casespublic function testServiceWillThrowRuntimeExceptionWhenStorageFailsToFetchTaskList() { // Throw a runtime exception when connection to storage fails for fetching task list }
public function testServiceWillThrowInvalidArgumentExceptionWhenInvalidTaskIsAdded() { // Throw an invalid argument exception for invalid task when adding }
public function testServiceWillThrowRuntimeExceptionWhenStorageFails() { // Throw a runtime exception when storage of task fails }
public function testServiceWillThrowDomainExceptionWhenTaskWasMarkedAsDoneWhenMarkingTaskAsDone() { // Throw a domain exception when a task was already marked as done }
34
![Page 35: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/35.jpg)
QuestionWhy am I using very long and explicit method names for
my test methods?
![Page 36: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/36.jpg)
AnswerTo have human readable documentation about the features we’re developing and testing.
./vendor/bin/phpunit--testdox
![Page 37: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/37.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
TestDox outputPHPUnit 6.1.3 by Sebastian Bergmann and contributors.
App\Test\Service\TaskService [ ] Service returns list of tasks [ ] Service can add new task [ ] Service throws exception if task was not found [ ] Service can find task [ ] Service can remove task [ ] Service can update existing task [ ] Service can mark task as done [ ] Service can remove task marked as done [ ] Service will throw type error when invalid task is added [ ] Service will throw domain exception when done task gets marked done
37
![Page 38: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/38.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development38
ExerciseComplete the test cases
gitcheckouttdd-ex1
Go and check out branch tdd-ex1 where you will find the code as we’ve seen thus far.
Pro tip: complete 1 test and commit, this way you also learn to commit small and commit often.
20 minutes
![Page 39: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/39.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
How we’re approaching thisWe need to “prepare” our test class
Create a “setUp” method to create a fixture
Create a “tearDown” method to unset the fixture
Implement first test “testServiceReturnsListOfTasks”
Making use of fixture to mimic actual behaviour
Create class interfaces for structure
Implement concrete class
39
![Page 40: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/40.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Preparing our 1st test
40
![Page 41: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/41.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
class TaskServiceTest extends TestCase { protected $taskGateway;
protected function setUp() { parent::setUp();
$taskEntity = $this->getMockBuilder(TaskEntityInterface::class) ->setMethods(['getId', 'getLabel', 'getDescription', 'isDone', 'getCreated', 'getModified'])->getMock();
$taskEntry1 = clone $taskEntity; $taskEntry1->method('getId')->willReturn('123'); $taskEntry1->method('getLabel')->willReturn('Task #123'); $taskEntry1->method('getDescription')->willReturn('#123: This is task 123'); $taskEntry1->method('isDone')->willReturn(false); $taskEntry1->method('getCreated')->willReturn(new \DateTime('2017-03-21 07:53:24')); $taskEntry1->method('getModified')->willReturn(new \DateTime('2017-03-21 08:16:53'));
$taskEntry2 = clone $taskEntity; $taskEntry3 = clone $taskEntity;
$taskCollection = new \SplObjectStorage(); $taskCollection->attach($taskEntry3); $taskCollection->attach($taskEntry2); $taskCollection->attach($taskEntry1);
$taskGateway = $this->getMockBuilder(TaskGatewayInterface::class)->setMethods(['fetchAll'])->getMock(); $taskGateway->expects($this->any())->method('fetchAll')->willReturn($taskCollection); $this->taskGateway = $taskGateway; } /* ... */ }
41
![Page 42: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/42.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
class TaskServiceTest extends TestCase { /* ... */
protected function tearDown() { unset ($this->taskGateway); }
/* ... */ }
42
![Page 43: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/43.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
class TaskServiceTest extends TestCase { /* ... */
/** * List open tasks sorted newest to oldest * * @covers TaskService::fetchAll */ public function testServiceReturnsListOfTasks() { $taskService = new TaskService($this->taskGateway); $taskList = $taskService->getAllTasks();
$this->assertInstanceOf(\Iterator::class, $taskList); $this->assertGreaterThan(0, count($taskList)); $taskList->rewind(); $previous = null; while ($taskList->valid()) { if (null !== $previous) { $current = $taskList->current(); $this->assertTrue($previous->getCreated() > $current->getCreated()); } $previous = $taskList->current(); $taskList->next(); } }
/* ... */ }
43
![Page 44: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/44.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Creating gateway interface<?php
namespace App\Model;
interface TaskGatewayInterface { /** * Fetch all tasks from the back-end storage * @return \Iterator */ public function fetchAll(): \Iterator; }
44
![Page 45: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/45.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Creating entity interface<?php
namespace App\Model;
interface TaskEntityInterface { public function getId(): string; public function getLabel(): string; public function getDescription(): string; public function isDone(): bool; public function getCreated(): \DateTime; public function getModified(): \DateTime; }
45
![Page 46: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/46.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development46
![Page 47: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/47.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development47
ExerciseCheck out the finished test cases
gitcheckouttdd-ex2
Go and check out branch tdd-ex2 where you will find the completed test cases.
Make sure you also check out the GIT logs as I used 27 commits to explain what was happening and why!
10 minutes
![Page 48: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/48.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
protected function setUp() { parent::setUp();
// We create a mock object $taskEntity = $this->getMockBuilder(TaskEntityInterface::class) ->setMethods(['getId', 'getLabel', 'getDescription', 'isDone', 'getCreated', 'getModified', 'setLabel', 'setDone']) ->getMock();
$taskEntry1 = clone $taskEntity; $taskEntry1->method('getId')->willReturn('123'); $taskEntry1->method('getLabel')->willReturn('Task #123'); $taskEntry1->method('getDescription')->willReturn('#123: This is task 123'); $taskEntry1->method('isDone')->willReturn(false); $taskEntry1->method('getCreated')->willReturn(new \DateTime('2017-03-21 07:53:24')); $taskEntry1->method('getModified')->willReturn(new \DateTime('2017-03-21 08:16:53'));
$taskEntryUpdate = clone $taskEntity; $taskEntryUpdate->method('getId')->willReturn('123'); $taskEntryUpdate->method('getLabel')->willReturn('Task #123: Update from service'); $taskEntryUpdate->method('getDescription')->willReturn('#123: This is task 123'); $taskEntryUpdate->method('isDone')->willReturn(false); $taskEntryUpdate->method('getCreated')->willReturn(new \DateTime('2017-03-21 07:53:24')); $taskEntryUpdate->method('getModified')->willReturn(new \DateTime('now'));
$taskEntry2 = clone $taskEntity; $taskEntry2->method('getId')->willReturn('456'); $taskEntry2->method('getLabel')->willReturn('Task #456'); $taskEntry2->method('getDescription')->willReturn('#456: This is task 456'); $taskEntry2->method('isDone')->willReturn(true); $taskEntry2->method('getCreated')->willReturn(new \DateTime('2017-03-22 07:53:24')); $taskEntry2->method('getModified')->willReturn(new \DateTime('2017-03-22 08:16:53'));
$taskEntry3 = clone $taskEntity; $taskEntry3->method('getId')->willReturn('789'); $taskEntry3->method('getLabel')->willReturn('Task #789'); $taskEntry3->method('getDescription')->willReturn('#789: This is task 789'); $taskEntry3->method('isDone')->willReturn(false); $taskEntry3->method('getCreated')->willReturn(new \DateTime('2017-04-23 07:53:24')); $taskEntry3->method('getModified')->willReturn(new \DateTime('2017-04-23 08:16:53'));
$taskEntryDone = clone $taskEntity; $taskEntryDone->method('getId')->willReturn('789'); $taskEntryDone->method('getLabel')->willReturn('#789'); $taskEntryDone->method('getDescription')->willReturn('#789: This is task 789'); $taskEntryDone->method('isDone')->willReturn(true); $taskEntryDone->method('getCreated')->willReturn(new \DateTime('2017-04-23 07:53:24')); $taskEntryDone->method('getModified')->willReturn(new \DateTime('now'));
48
![Page 49: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/49.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Magic happening in setUpIdeal place to set things up (using fixtures)
Stub is shared among different test methods
Now all is ready to be implemented as we secured the code-base
49
![Page 50: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/50.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Project statusApp\Test\Service\TaskService [x] Service returns list of tasks [x] Service can add new task [x] Service throws exception if task was not found [x] Service can find task [x] Service can remove task [x] Service can update existing task [x] Service can mark task as done [x] Service can remove task marked as done [x] Service will throw type error when invalid task is added [x] Service will throw domain exception when done task gets marked done
50
![Page 51: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/51.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development51
ExerciseCheck out the finished test cases
gitcheckouttdd-ex3
Go and check out branch tdd-ex3 where you will find the other completed test cases.
15 minutes
![Page 52: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/52.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development52
![Page 53: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/53.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
TextDox outputPHPUnit 6.1.3 by Sebastian Bergmann and contributors.
App\Test\Model\TaskEntity [x] Task entity is empty at construction [x] Task entity throws error when constructed with wrong type of arguments [x] Task entity throws exception when constructed with wrong arguments [x] Task entity accepts correct arguments
App\Test\Model\TaskGateway [x] Fetch all returns iterator object [x] Gateway can add task entity [x] Find returns null when nothing found [x] Find returns task entity when result is found
[x] Gateway can remove task entity [x] Gateway can update task entity
App\Test\Service\TaskService [x] Service returns list of tasks [x] Service can add new task [x] Service throws exception if task was not found [x] Service can find task [x] Service can remove task [x] Service can update existing task [x] Service can mark task as done [x] Service can remove task marked as done [x] Service will throw type error when invalid task is added [x] Service will throw domain exception when done task gets marked done
53
![Page 54: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/54.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development54
ExerciseCheck out the web app
gitcheckouttdd-ex4php-Slocalhost:8000-twebweb/index.php
Go and check out branch tdd-ex4 where you will find the other completed test cases.
Run the web application using PHP’s build-in web server to see how the app is behaving.
15 minutes
![Page 55: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/55.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development55
![Page 56: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/56.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development56
![Page 57: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/57.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development57
![Page 58: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/58.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development58
![Page 59: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/59.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development59
ConclusionLet’s recap what has happened here
Writing test-first gives you a clean scope of what your code should do.
You have a more precise code-base that’s easy to maintain, upgrade and is independent.
![Page 60: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/60.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Agenda
60
Introduction
TDD from scratch
TDD with legacy app
Additional tips
Recap
![Page 61: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/61.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Legacy challengesNot (always) written with testing in mind
Dependencies make it hard to change code
Refactoring is often required before proper testing can start
For refactoring tests are required to ensure the refactored code behaves the same!
61
![Page 62: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/62.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Example project: EPESI
62
![Page 63: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/63.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development63
![Page 64: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/64.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Tests?!?
64
![Page 65: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/65.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Tests?!?
64
![Page 66: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/66.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
How to get started?
65
![Page 67: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/67.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Getting ready to test!<?xmlversion="1.0"encoding="UTF-8"?>
<phpunitbootstrap="vendor/autoload.php"colors="true"stopOnErrors="true"stopOnFailures="true">
<testsuites><testsuitename="Appunittests"><directorysuffix="php">tests</directory></testsuite></testsuites>
<filter><whitelist><directorysuffix="php">src</directory></whitelist></filter>
<logging><logtype="coverage-html"target="build/coverage"lowUpperBound="35"highLowerBound="70"/></logging>
</phpunit>
66
![Page 68: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/68.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
ModuleManager::module_install/** * Includes file with module installation class. * * Do not use directly. * * @param string $module_class_name module class name - underscore separated */ public static final function include_install($module_class_name) { if(isset(self::$modules_install[$module_class_name])) return true; $path = self::get_module_dir_path($module_class_name); $file = self::get_module_file_name($module_class_name); $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; if (!file_exists($full_path)) return false; ob_start(); $ret = require_once($full_path); ob_end_clean(); $x = $module_class_name.'Install'; if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); self::$modules_install[$module_class_name] = new $x($module_class_name); return true; }
67
![Page 69: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/69.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Testing happily…<?php
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../src/ModuleManager.php';
class ModuleManagerTest extends TestCase { /** * @covers ModuleManager::include_install */ public function testModuleManagerCanLoadMailModule() { $result = \ModuleManager::include_install('Mail'); $this->assertTrue($result); } }
68
![Page 70: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/70.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development69
![Page 71: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/71.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Comment out the test<?php
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../src/ModuleManager.php';
class ModuleManagerTest extends TestCase { /** * @covers ModuleManager::include_install */ /*public function testModuleManagerCanLoadMailModule() { $result = \ModuleManager::include_install('Mail'); $this->assertTrue($result); }*/
}
70
![Page 72: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/72.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Let’s look again, more closely/** * Includes file with module installation class. * * Do not use directly. * * @param string $module_class_name module class name - underscore separated */ public static final function include_install($module_class_name) { if(isset(self::$modules_install[$module_class_name])) return true; $path = self::get_module_dir_path($module_class_name); $file = self::get_module_file_name($module_class_name); $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; if (!file_exists($full_path)) return false; ob_start(); $ret = require_once($full_path); ob_end_clean(); $x = $module_class_name.'Install'; if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); self::$modules_install[$module_class_name] = new $x($module_class_name); return true; }
71
![Page 73: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/73.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Let’s look again, more closely/** * Includes file with module installation class. * * Do not use directly. * * @param string $module_class_name module class name - underscore separated */ public static final function include_install($module_class_name) { if(isset(self::$modules_install[$module_class_name])) return true; $path = self::get_module_dir_path($module_class_name); $file = self::get_module_file_name($module_class_name); $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; if (!file_exists($full_path)) return false; ob_start(); $ret = require_once($full_path); ob_end_clean(); $x = $module_class_name.'Install'; if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); self::$modules_install[$module_class_name] = new $x($module_class_name); return true; }
71
![Page 74: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/74.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Let’s look again, more closely/** * Includes file with module installation class. * * Do not use directly. * * @param string $module_class_name module class name - underscore separated */ public static final function include_install($module_class_name) { if(isset(self::$modules_install[$module_class_name])) return true; $path = self::get_module_dir_path($module_class_name); $file = self::get_module_file_name($module_class_name); $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; if (!file_exists($full_path)) return false; ob_start(); $ret = require_once($full_path); ob_end_clean(); $x = $module_class_name.'Install'; if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); self::$modules_install[$module_class_name] = new $x($module_class_name); return true; }
71
![Page 75: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/75.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Let’s look again, more closely/** * Includes file with module installation class. * * Do not use directly. * * @param string $module_class_name module class name - underscore separated */ public static final function include_install($module_class_name) { if(isset(self::$modules_install[$module_class_name])) return true; $path = self::get_module_dir_path($module_class_name); $file = self::get_module_file_name($module_class_name); $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; if (!file_exists($full_path)) return false; ob_start(); $ret = require_once($full_path); ob_end_clean(); $x = $module_class_name.'Install'; if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); self::$modules_install[$module_class_name] = new $x($module_class_name); return true; }
71
![Page 76: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/76.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Let’s look again, more closely/** * Includes file with module installation class. * * Do not use directly. * * @param string $module_class_name module class name - underscore separated */ public static final function include_install($module_class_name) { if(isset(self::$modules_install[$module_class_name])) return true; $path = self::get_module_dir_path($module_class_name); $file = self::get_module_file_name($module_class_name); $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; if (!file_exists($full_path)) return false; ob_start(); $ret = require_once($full_path); ob_end_clean(); $x = $module_class_name.'Install'; if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); self::$modules_install[$module_class_name] = new $x($module_class_name); return true; }
71
![Page 77: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/77.jpg)
http
s://w
ww.
flick
r.com
/pho
tos/
mar
cgbx
/780
3086
292
![Page 78: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/78.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Test first condition/** * @covers ModuleManager::include_install */ public function testReturnImmediatelyWhenModuleAlreadyLoaded() { $module = 'Foo_Bar'; ModuleManager::$modules_install[$module] = 1; $result = ModuleManager::include_install($module); $this->assertTrue($result); $this->assertCount(1, ModuleManager::$modules_install); }
73
![Page 79: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/79.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development74
![Page 80: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/80.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development75
![Page 81: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/81.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development76
http
s://w
ww.
flick
r.com
/pho
tos/
chris
tian_
joha
nnes
en/2
2482
4478
6
![Page 82: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/82.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Test second condition/** * @covers ModuleManager::include_install */ public function testReturnWhenModuleIsNotFound() { $module = 'Foo_Bar'; $result = ModuleManager::include_install($module); $this->assertFalse($result); $this->assertEmpty(ModuleManager::$modules_install); }
77
![Page 83: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/83.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development78
![Page 84: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/84.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
/** * Includes file with module installation class. * * Do not use directly. * * @param string $module_class_name module class name - underscore separated */ public static final function include_install($module_class_name) { if(isset(self::$modules_install[$module_class_name])) return true; $path = self::get_module_dir_path($module_class_name); $file = self::get_module_file_name($module_class_name); $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; if (!file_exists($full_path)) return false; ob_start(); $ret = require_once($full_path); ob_end_clean(); $x = $module_class_name.'Install'; if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); self::$modules_install[$module_class_name] = new $x($module_class_name); return true; }
79
![Page 85: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/85.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
/** * Includes file with module installation class. * * Do not use directly. * * @param string $module_class_name module class name - underscore separated */ public static final function include_install($module_class_name) { if(isset(self::$modules_install[$module_class_name])) return true; $path = self::get_module_dir_path($module_class_name); $file = self::get_module_file_name($module_class_name); $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; if (!file_exists($full_path)) return false; ob_start(); $ret = require_once($full_path); ob_end_clean(); $x = $module_class_name.'Install'; if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); self::$modules_install[$module_class_name] = new $x($module_class_name); return true; }
79
self::$modules_install[$module_class_name]
![Page 86: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/86.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Add a “setUp” methodclass ModuleManagerTest extends TestCase { protected function setUp() { ModuleManager::$modules_install = []; } /* ... */ }
80
![Page 87: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/87.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development81
![Page 88: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/88.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development82
http
s://w
ww.
flick
r.com
/pho
tos/
evae
kebl
ad/1
4780
0905
50
![Page 89: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/89.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Testing third condition/** * @covers ModuleManager::include_install * @expectedException Error */ public function testTriggerErrorWhenInstallClassDoesNotExists() { $module = 'EssClient'; $result = ModuleManager::include_install($module); $this->fail('Expecting loading module ' . $module . ' would trigger an error'); }
83
![Page 90: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/90.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development84
![Page 91: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/91.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
/** * Includes file with module installation class. * * Do not use directly. * * @param string $module_class_name module class name - underscore separated */ public static final function include_install($module_class_name) { if(isset(self::$modules_install[$module_class_name])) return true; $path = self::get_module_dir_path($module_class_name); $file = self::get_module_file_name($module_class_name); $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; if (!file_exists($full_path)) return false; ob_start(); $ret = require_once($full_path); ob_end_clean(); $x = $module_class_name.'Install'; if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); self::$modules_install[$module_class_name] = new $x($module_class_name); return true; }
85
![Page 92: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/92.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
/** * Includes file with module installation class. * * Do not use directly. * * @param string $module_class_name module class name - underscore separated */ public static final function include_install($module_class_name) { if(isset(self::$modules_install[$module_class_name])) return true; $path = self::get_module_dir_path($module_class_name); $file = self::get_module_file_name($module_class_name); $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; if (!file_exists($full_path)) return false; ob_start(); $ret = require_once($full_path); ob_end_clean(); $x = $module_class_name.'Install'; if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); self::$modules_install[$module_class_name] = new $x($module_class_name); return true; }
85
if (!file_exists($full_path)) return false;
![Page 93: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/93.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Our current file structure|-- ModuleManager.php `-- modules |-- EssClient | `-- EssClient.php |-- IClient | `-- IClientInstall.php `-- Mail `-- MailInstall.php
86
![Page 94: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/94.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development87
http
s://w
ww.
flick
r.com
/pho
tos/
sis/
2497
9123
43
Dead Code
![Page 95: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/95.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
So this test…/** * @covers ModuleManager::include_install * @expectedException Error */ public function testTriggerErrorWhenInstallClassDoesNotExists() { $module = 'EssClient'; $result = ModuleManager::include_install($module); $this->fail('Expecting loading module ' . $module . ' would trigger an error'); }
88
![Page 96: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/96.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
… changes into this test/** * @covers ModuleManager::include_install */ public function testTriggerErrorWhenInstallClassDoesNotExists() { $module = 'EssClient'; $result = ModuleManager::include_install($module); $this->assertFalse($result); }
89
![Page 97: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/97.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development90
http
s://w
ww.
flick
r.com
/pho
tos/
fragi
lete
nder
/533
2586
299
![Page 98: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/98.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Our current file structure|-- ModuleManager.php `-- modules |-- EssClient | `-- EssClient.php |-- IClient | `-- IClientInstall.php `-- Mail `-- MailInstall.php
91
![Page 99: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/99.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Testing fourth condition/** * @covers ModuleManager::include_install * @expectedException Error */ public function testTriggerErrorWhenInstallClassIsNotRegistered() { $module = 'IClient'; $result = ModuleManager::include_install($module); $this->fail('Expecting loading module ' . $module . ' would trigger an error'); }
92
![Page 100: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/100.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development93
![Page 101: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/101.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development94
![Page 102: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/102.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Completing all tests
95
![Page 103: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/103.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Remove comment tags/** * @covers ModuleManager::include_install */ public function testModuleManagerCanLoadMailModule() { $result = \ModuleManager::include_install('Mail'); $this->assertTrue($result); }
96
![Page 104: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/104.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development97
![Page 105: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/105.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development98
![Page 106: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/106.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
RecapTesting legacy code is not easy, but still possible
Approach the source-code with a bail-first approach
Make sure you can “bail” the method as fast as possible
Start with the most important part of your code
Ask yourself “What costs us money if it breaks” ➡ test that first!
99
![Page 107: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/107.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development100
ExerciseCheck out these tests
gitcheckoutlegacy-0.1
Go and check out branch legacy-0.1 and analyse the tests.
If you have XDebug installed, you can run PHPUnit with code coverage.
15 minutes
![Page 108: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/108.jpg)
What to do?If your code doesn’t return values
![Page 109: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/109.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
/** * Process Bank Payment files */ public function processBankPayments() { $this->getLogger()->log('Starting bank payment process', Zend_Log::INFO); foreach ($this->_getBankFiles() as $bankFile) { $bankData = $this->_processBankFile($bankFile); $this->getLogger()->log('Processing ' . $bankData->transactionId, Zend_Log::DEBUG); /** @var Contact_Model_Contact $contact */ $contact = $this->getMapper('Contact_Model_Mapper_Contact') ->findContactByBankAccount($bankData->transactionAccount); if (null !== $contact) { $this->getLogger()->log(sprintf('Found contact "%s" for bank account %s', $contact->getName(),$bankData->transactionAccount ), Zend_Log::DEBUG); $data = array ( 'amount' => $bankData->transactionAmount, 'payment_date' => $bankData->transactionDate ); $this->getMapper('Invoice_Model_Mapper_Payments') ->updatePayment($data, array ('contact_id = ?' => $contact->getContactId())); $this->_moveBankFile($bankFile, $this->getPath() . DIRECTORY_SEPARATOR . self::PROCESS_SUCCEEDED ); } else { $this->getLogger()->log(sprintf( 'Could not match bankaccount "%s" with a contact', $bankData->transactionAccount ), Zend_Log::WARN); $this->_moveBankFile($bankFile, $this->getPath() . DIRECTORY_SEPARATOR . self::PROCESS_FAILED ); } } }
102
![Page 110: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/110.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development103
![Page 111: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/111.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
class Payments_Service_As400Test extends \PHPUnit\Framework\TestCase { public function testProcessingBankPayments() { $contact = $this->getMockBuilder(Contact_Model_Contact::class) ->setMethods(['getContactId', 'getName']) ->getMock();
$contact->expects($this->any()) ->method('getContactId') ->will($this->returnValue(1));
$contact->expects($this->any()) ->method('getName') ->will($this->returnValue('Foo Bar'));
$contactMapper = $this->getMockBuilder(Contact_Model_Mapper_Contact::class) ->setMethods(['findContactByBankAccount']) ->getMock();
$contactMapper->expects($this->any()) ->method('findContactByBankAccount') ->will($this->returnValue($contact));
$paymentsMapper = $this->getMockBuilder(Invoice_Model_Mapper_Payments::class) ->setMethods(['updatePayment']) ->getMock();
$logMock = new Zend_Log_Writer_Mock(); $logger = new Zend_Log(); $logger->setWriter($logMock); $logger->setPriority(Zend_Log::DEBUG);
$as400 = new Payments_Service_As400(); $as400->addMapper($contactMapper, Contact_Model_Mapper_Contact::class) ->addMapper($paymentsMapper, Invoice_Model_Mapper_Payments::class) ->setPath(__DIR__ . DIRECTORY_SEPARATOR . '_files') ->setLogger($logger);
$as400->processBankPayments(); $this->assertCount(3, $logMock->events); $this->assertEquals('Processing 401341345', $logMock->events[1]); $this->assertEquals( 'Found contact "Foo Bar" for bank account BE93522511513933', $logMock->events[2] ); } }
104
![Page 112: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/112.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Let’s focus on mocks first$contact = $this->getMockBuilder(Contact_Model_Contact::class) ->setMethods(['getContactId', 'getName']) ->getMock();
$contact->expects($this->any()) ->method('getContactId') ->will($this->returnValue(1));
$contact->expects($this->any()) ->method('getName') ->will($this->returnValue('Foo Bar'));
$contactMapper = $this->getMockBuilder(Contact_Model_Mapper_Contact::class) ->setMethods(['findContactByBankAccount']) ->getMock();
$contactMapper->expects($this->any()) ->method('findContactByBankAccount') ->will($this->returnValue($contact));
$paymentsMapper = $this->getMockBuilder(Invoice_Model_Mapper_Payments::class) ->setMethods(['updatePayment']) ->getMock();
105
![Page 113: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/113.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Write the log mock$logMock = new Zend_Log_Writer_Mock(); $logger = new Zend_Log(); $logger->setWriter($logMock); $logger->setPriority(Zend_Log::DEBUG);
$as400 = new Payments_Service_As400(); $as400->addMapper($contactMapper, Contact_Model_Mapper_Contact::class) ->addMapper($paymentsMapper, Invoice_Model_Mapper_Payments::class) ->setPath(__DIR__ . DIRECTORY_SEPARATOR . '_files') ->setLogger($logger);
$as400->processBankPayments(); $this->assertCount(3, $logMock->events); $this->assertEquals('Processing 401341345', $logMock->events[1]); $this->assertEquals( 'Found contact "Foo Bar" for bank account BE93522511513933', $logMock->events[2] );
106
![Page 114: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/114.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development107
![Page 115: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/115.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development108
ExerciseCheck out the web app
gitcheckoutlegacy-0.2
Go and check out branch legacy-0.2 where you will find the example test case.
10 minutes
![Page 116: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/116.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Privates Exposed
109
http
://w
ww.
slas
hgea
r.com
/form
er-ts
a-ag
ent-a
dmits
-we-
knew
-full-
body
-sca
nner
s-di
dnt-w
ork-
3131
5288
/
![Page 117: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/117.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Direct access forbidden?!?<?php
defined("_VALID_ACCESS") || die('Direct access forbidden');
/** * This class provides dependency requirements * @package epesi-base * @subpackage module */ class Dependency {
private $module_name; private $version_min; private $version_max; private $compare_max;
private function __construct( $module_name, $version_min, $version_max, $version_max_is_ok = true) { $this->module_name = $module_name; $this->version_min = $version_min; $this->version_max = $version_max; $this->compare_max = $version_max_is_ok ? '<=' : '<'; }
/** ... */ }
110
![Page 118: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/118.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Don’t touch my junk!
111
http
s://w
ww.
flick
r.com
/pho
tos/
case
ymul
timed
ia/5
4122
9373
0
![Page 119: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/119.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
House of Reflection
http
s://w
ww.
flick
r.com
/pho
tos/
tabo
r-roe
der/8
2507
7011
5
![Page 120: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/120.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Let’s do this…<?php require_once 'include.php';
class DependencyTest extends PHPUnit_Framework_TestCase { public function testConstructorSetsProperSettings() { require_once 'include/module_dependency.php';
// We have a problem, the constructor is private! } }
113
![Page 121: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/121.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Using static method works too$params = array ( 'moduleName' => 'Foo_Bar', 'minVersion' => 0, 'maxVersion' => 1, 'maxOk' => true, ); // We use a static method for this test $dependency = Dependency::requires_range( $params['moduleName'], $params['minVersion'], $params['maxVersion'], $params['maxOk'] );
// We use reflection to see if properties are set correctly $reflectionClass = new ReflectionClass('Dependency');
114
![Page 122: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/122.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Asserting private properties// Let's retrieve the private properties $moduleName = $reflectionClass->getProperty('module_name'); $moduleName->setAccessible(true); $minVersion = $reflectionClass->getProperty('version_min'); $minVersion->setAccessible(true); $maxVersion = $reflectionClass->getProperty('version_max'); $maxVersion->setAccessible(true); $maxOk = $reflectionClass->getProperty('compare_max'); $maxOk->setAccessible(true);
// Let's assert $this->assertEquals($params['moduleName'], $moduleName->getValue($dependency), 'Expected value does not match the value set’); $this->assertEquals($params['minVersion'], $minVersion->getValue($dependency), 'Expected value does not match the value set’); $this->assertEquals($params['maxVersion'], $maxVersion->getValue($dependency), 'Expected value does not match the value set’); $this->assertEquals('<=', $maxOk->getValue($dependency), 'Expected value does not match the value set');
115
![Page 123: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/123.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development116
![Page 124: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/124.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Agenda
117
Introduction
TDD from scratch
TDD with legacy app
Additional tips
Recap
![Page 125: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/125.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
PHPUnit requires basic PHP!Sometimes the challenge lies within PHP instead of direct PHPUnit
Testing is simple, coding is hard!
Testing is all about asserting that an actual process matches an expected result, so make sure you cover your expectations and test against those expectations
PHP functionality you need to know:
Reflection
Streams
System and PHP executions (e.g. “eval”, “passthru”, …)
118
![Page 126: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/126.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
If you don’t know the destination…Start testing with what you know
Work your way up
119
![Page 127: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/127.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
“But my code is too crappy…”
120
![Page 128: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/128.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
For “untestable” codeWrite out the functionality in tests
Create a class providing this functionality (service, model, …)
Slowly move your existing code over to use the “cleaner” code
Bonus: you’ve got it already tested
121
![Page 129: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/129.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Agenda
122
Introduction
TDD from scratch
TDD with legacy app
Additional tips
Recap
![Page 130: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/130.jpg)
Everything is testable!Not always easy, but always possible
![Page 131: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/131.jpg)
Write your tests firstWrite your code based on your tests
![Page 132: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/132.jpg)
Use code coverage as guideIt shows your progress through the code
![Page 133: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/133.jpg)
Be creative!Sometimes PHP can help out
![Page 134: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/134.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Recommended reading
127
![Page 135: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/135.jpg)
GitHub Repogithub.com/in2it-training/tdd-workshop
![Page 136: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/136.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development
Thank you
129
http
s://w
ww.
flick
r.com
/pho
tos/
drew
m/3
1918
7251
5
![Page 137: Let your tests drive your code](https://reader038.vdocuments.us/reader038/viewer/2022103010/5a65e0cf7f8b9aaf638b55f3/html5/thumbnails/137.jpg)
in2it training - www.in2it.be - @in2itvof - #in2tdd Let your tests drive your development130
in it2PROFESSIONAL PHP SERVICES
Michelangelo van DamZend Certified Engineer
[email protected] - www.in2it.be - T in2itvof - F in2itvof
Quality Assurance
Zend Framework 3Consulting
Disaster Recovery
Development Workflow
EnterprisePHP
TrainingMentoring
Our expertise services