Building a PHP Framework: Part 5 – Test Driven Development
In part 4 we laid the foundation for Analyze. Now it’s time to write the very first lines of code!
Before we continue, please allow me to interrupt with a quick announcement: I’ve started a newsletter! Each week I’ll send you a great email filled with updates, great links, tips & tricks, and other non-dev randomness. If you’re interested you can sign up using the form in the sidebar to the right 👉 or follow this link.
What is Test Driven Development?
Let’s get the Wikipedia definition out of the way:
Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: requirements are turned into very specific test cases, then the software is improved to pass the new tests, only. This is opposed to software development that allows software to be added that is not proven to meet requirements.
Great. So what does that mean?
Essentially, test driven development is a process of writing tests before writing any code, then writing code that passes the tests.
- Write test
- Run tests, see new one fail
- Write code that passes
- Run tests
- Refactor
If you’re new to TDD, this probably seems pretty weird. Stick with me.
Tools For Test Driven Development
I will be using PHPUnit, a testing framework, to do all of my testing. For instructions on how to install PHPUnit, please see their documentation.
Note: I’m barely touching the surface of what PHPUnit can do in this post. I highly encourage you to read through the documentation.
A Simple Example
Let’s say you want to build a class that takes two numbers and adds them. Let’s write a test to ensure that our add method returns the correct result. Keep in mind, this example is initially simple to illustrate how PHPUnit works on a basic level.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php use PHPUnit\Framework\TestCase; class ExampleTest extends TestCase { public function testTwoNumbersAreAdded() { $adder = new NumberAdder(1, 2); $result = $adder->add(); $this->assertEquals(3, $result); } } |
In this test we create a new NumberAdder object, passing the two numbers we want to manipulate (1 and 2). From there we add the numbers together and tell PHPUnit that we expect the result to equal 3. Let’s run it:
It, of course, fails giving us:
1 |
Error: Class 'NumberAdder' not found |
Following the TDD method, let’s create the class and get rid of that error.
1 2 3 4 5 6 7 |
<?php namespace NumberAdder; class NumberAdder { } |
Next, import it into our ExampleTest:
1 |
use NumberAdder\NumberAdder; |
Now run it again:
Nice, the error is gone and we have a new one:
1 |
Error: Call to undefined method NumberAdder\NumberAdder::add() |
That makes sense, we haven’t created it. Let’s do that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php namespace NumberAdder; class NumberAdder { public $firstNumber; public $secondNumber; public function __construct(int $firstNumber, int $secondNumber) { $this->firstNumber = $firstNumber; $this->secondNumber = $secondNumber; } public function add() : int { } } |
Alright, the previous error is gone. Now we get:
1 |
Failed asserting that null matches expected 3. |
Our add method doesn’t return anything, hence the null, and null doesn’t equal 3. One last fix:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php namespace NumberAdder; class NumberAdder { public $firstNumber; public $secondNumber; public function __construct(int $firstNumber, int $secondNumber) { $this->firstNumber = $firstNumber; $this->secondNumber = $secondNumber; } public function add() : int { return $this->firstNumber + $this->secondNumber; } } |
It passes!
The First Analyze Test
The previous example was a very simplified rundown of the TDD process. Write a test, run, it fails, make it not fail, run again, repeat. I’ll be doing that the entire way as I build the Analyze PHP Framework. Here’s the first one:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php use PHPUnit\Framework\TestCase; use Analyze\Application; class ApplicationTest extends TestCase { public function testApplicationInstanceIsReturned() { $app = new Application; $this->assertInstanceOf(Application::class, $app); } } |
We are on our way! Be sure to keep track of the Framework repository.
Further Reading
Grumpy Learning
Introduction to Test Driven Development (TDD)
PHPUnit Tutorial
Update: Building a PHP Framework: Part 6 – Dependency Inversion, Inversion of Control, oh my!