In this article we will see how using Test Driven Development (TDD, explained in deep a little later in the article) helps to write good quality code that is maintainable, reusable, and easy to debug and fix when bugs happen.
PHP and Testing
PHP and testing didn’t start together as friends. We all know that other languages are more oriented from the start to create code based on testing suites, but PHP was conceived for quick development. That’s why testing wasn’t part of the day to day routine in a PHP coder.
But times are changing, and now PHP is a mature language that can be used to build great and big applications, and using a test driven development approach is the way to go if you want to save time at the end and build good quality code.
What Is TDD?
TDD stands for Test Driven Development. Basically this means: write your test cases first, and your code later. At first, your tests will fail, because the code will be empty for that case, and that’s fine. Your mission here is making the test pass by building the code to make it happen. This allows you to focus your coding process to pass well described specs, instead of having it all in your head while coding (points in favor for when you need to work under pressure or you’re tired).
More specifically, the pattern behind TDD can be summarized by the following flow chart:

So I summarize this as:
- Write your test case
- Write code for your test code
- Test
- Fix and refactor
- Move to next test case
For those out there following an agile approach, this sounds very similar. Well it is, because TDD is suited for agile development.
Of course, things could go wrong, but if you found a bug later you have to write another test case, clearly missing from your suite. The good part of this is that when writing new features, they will have to also pass the current automated tests.
Unit Testing
Ok, we know what TDD means, but what about Unit testing? Unit testing means testing part of your code isolated from the rest. For that, you need to improve your code to be able to test each component alone, without any interference from other components.
This is why TDD is great for development, because it force developers to isolate the different pieces of information in well known methods that can be tested individually to pass the tests.
It’s not difficult to imagine that TDD will be difficult to implement with code that makes use of God objects, or include too many concerns in one function or method. For example, a function that process a XML file and imports data into a database. You could split that into several functions, one that process the XML, another one for getting the results mapped to the database, and another for saving and checking for errors. If you do it all in one method, then it’s hard to test if that method is doing the task right. As a general rule, each method should do just one thing, and do it right.
That’s how you can do unit testing, by creating small and well defined methods with a clear task to implement, test, and prove right or wrong.
Dependency Injection
So all this sounds great, but now when you need to create code for testing, you find out that your function depends on different classes, and those classes are instantiated into the function. What should you do? How do you know the problem is not in those classes? Should you write a test suite for every library you’re using?
Well no, although you may want to use libraries that have a test suite now, don’t you?
The solution to this is dependency injection, a pattern that implements inversion of control. All these fancy words can be explained with an example, so let’s take the following function:
class Company {
public $members;
public function addMember($name, $position)
{
$member = new Member($name, $position);
$this->members[] = $member;
}
}
In this case, calling the method addMember
implies that a member object has to be created inside the function. So when we test that method, we have to test not only that the member has been added to the $members
array, but also that the constructor works as expected. Not very unit right?
Okay, so the solution for this problem is injecting the dependency in the call of the method:
class Company {
public $members;
public function addMember(Member $member)
{
$this->members[] = $member;
}
}
Now we only need to test if the array is adding the member. Having a valid $member
instance is not our problem here. So we test that individual piece of code. That’s unit testing.
In the test case, we can mock the $member
argument and pass it along. However, there are frameworks like Laravel that have solved this by creating methods to resolve dependencies on the fly, without the need to pass it in the arguments. Of course this is useful for injecting service providers more than our example above, but think on this example:
class Company {
public function sendNotificationEmail()
{
//create $html to send
$mail = new Mailer();
$mail->send($html);
}
}
Here you have a whole new problem with the mailer function being created inside the method. What you should do instead is this:
class Company {
public function sendNotificationEmail(Mailer $mail)
{
//create $html to send
$mail->send($html);
}
}
With service providers auto resolving themselves, you only need to do this call:
$company = new Company;
$company->sendNotificationEmail();
And the system will do the trick. And by the way, good looking semantic code right?
Introducing PHPUnit
PHPUnit, as you can cleverly deduce by the name, is a library for creating unit tests for our code. This is already included in some frameworks, and it is by far the more commonly used library for testing in PHP.
The purpose of this article is giving tips for improving your PHP code, so I’ll not enter into details of how to use PHPUnit. For that you can go to their detailed documentation.
If you followed the article so far, you know that you should write code that is independent, and that code is called testable.
Let’s take the Company example defined above, and write a test for it. For this you will have to create a file under a folder that we configure in our phpunit.xml
in the root of our project, after we install it. I recommend installing using Composer, but you can download the code and include it as you wish. So, for testing the add member function, we create the following test:
class CompanyTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider memberProvider
*/
public function testAddMember($company, $member, $expected)
{
// Act
$company->add($member);
// Assert
$this->assertEquals(count($company->members), $expected);
}
public function memberProvider()
{
$company = new Company;
return [
[$company, new Member, 1],
[$company, new Member, 2],
[$company, new Member, 3],
];
}
}
In this case, we are generating the test cases with a data provider that will return some information and the expected result. In our test case, we are using the function assertEquals
to test if the code is working as expected. We have a lot of assertions for us to use.
With PHPUnit we can do a lot of things: we can mock objects, databases, create data providers, create test dependencies, test exceptions, output, and a lot more. We can also test our REST APIs by doing calls to our endpoints. Check the full documentation to learn more.
Final Suggestions
My suggestion for using TDD is to do it every time you can. Most projects that you start from scratch are good for a TDD approach, because you can create them as you want.
However, projects with messy code or code that is build without considering the concepts we discussed in this article or that need to be corrected quickly to go in production are not always a good fit. What I suggest in those cases is to use TDD to refactor pieces of the code and improve the code by parts. Taking all the code and rebuilding everything is not an option in most cases.
TDD forces you to think in how to structure every function, and that’s good. With time you will find this task easier to do, and your code will be really easy to update and refactor.