Testing is a hot subject in the PHP world. It is not because testing hasn’t been around for eons, but more because the community is still pretty reluctant to accept it, and use in each and every project they may encounter. Main reasons for that might be that the adoption of newer PHP versions are really slow compared to other programming languages. For instance it took years to find PHP 5.3 in most hosting plans, there are still places where you still have 5.2 installed. When did the development of PHP 6 started? Again years ago. Now it became obvious that if you want to do the right thing, you embrace test driven development, and I will give the reasons to do that.
A little history lesson
The idea of testing has been floating around since the seventies. Only after 2000 a book was written with real test driven development implementation for Java. It wasn’t a mass hit, but today it is a classic. A similar book surfaced for C# a few years later. In web programming world the conception of Ruby programming language, and Ruby on Rails popularised the test driven development.
We are still not speaking about PHP. Why? Main culprits might be the PHP 4, and WordPress(sorry!). WordPress works without problem with older versions, and the code for plugins are usually just one page long, so many people thought they can handle the situation all by themselves, they don’t need additional tools, or any automation. Things are different now, we use social coding even for the tiniest projects, if somebody sends a pull request and you immediately merge, you have to cope with the consequences. Similarly a project may run for years, how do you know that a new feature plays nicely with the old ones.
Why bother with test driven development?
There are two things to consider when adopting testing methodologies:
1. Added layer of security
2. Better code
The first one is obvious: If you write your test correctly you can be sure that the code will work as excepted. Please, keep in mind that we are speaking about testing as a test driven development tool, so we are dealing with mistakes that might and will happen in production environment differently. Remember this concept, in the next section you’ll see names for various kinds of testing, all are used as a development technology in a development environment.
Second one: If you do test driven development you are able to write better code. In the first phase you don’t have to concentrate on implementation details, you first plan and write code that makes most sense to you(and other programmers, remember social coding). This being said TDD forces you to do a few things that people usually forget:
• Name the classes correctly
• Name the methods
• Use polymorphism
• One class – one task
We don’t live in the nineties, if you have ever checked out Quake 1 source code, you saw many clever tricks to make the magic happen. Nowadays most people hate clever code, if they can’t understand your code the first time they see it, they would most probably ditch it, because you are the only person who can maintain it in the long run.
Descriptive names for methods and classes is a must, this contributes to readability.
How does testing enforce polymorphism and the one class-one task principle? If you described MyClass as doing this, and that, and that, you are in trouble. While testing you must test all the possible execution paths, so if it makes sense to couple multiple functionality in one class you can do this by way of polymorphism. Let’s say a Calculator interface could be implemented by a Addition, Subtraction, and Multiplication classes. You add new functionality to those classes, and test them. Also a switch command might be an indicator that you need to refactor the code. Switch statements introduce multiple execution paths, and all of them should be tested.
Forms of testing
This is one way to divide test driven development. Of course we can argue until Christmas because the terminology is still not complete, but we need to start somewhere.
First Acceptance testing. I am not going to talk about it too much, basically it is out of scope of this article. Acceptance testing means that you are making sure that your client is content with the functionality provided by your application. There are frameworks for this task too, but the main thing here is that you ask the client what they want, you suggest what they need, and if you agree you describe those programatically.
The first law of unit testing is that you test in isolation. It means that you only test one class’s functionality and nothing more. You don’t instantiate other classes, and you never hit the database. If some class depends on others, you mock the functionality of the other. An excellent framework for that is Mockery, which you can find on Packagist, and install through Composer.
Wash, rinse, repeat
A cycle of test driven development looks like this.
- Write a test
- Watch it fail
- Write code to make the test pass
- Run the test again
- Write the production code
- Refactor if needed
The fourth step means that you don’t have to jump immediately to write production code. If you expect that a variable’s value is 4, you can hard-code it for the first time.
Here is a graphical representation from Wikipedia:
A birds eye view of testing with PHPUnit
This will be just a quick intro to PHPUnit, because it is a huge framework.
There are a couple of ways to install the framework, chances are that you already have it in your development stack(XMPP), through PEAR, or my preferred way, through Composer. Installing through Composer is intuitive if you are already using it for managing dependencies inside your project. If not you should be acquainted with it, there are hundreds of libraries that could be installed through Composer, Mockery needs it, Laravel needs it, you probably need it too. Composer installs are usually confined within one project, but, if you want to use PHPUnit globally, you can set up a central repository on your machine, and reference it in your project’s composer.json file.
There are a couple rules that should be kept in mind:
The structure of the test directory mirrors the application’s, so MyClass.php in src folder goes to tests/src. When you write your production code you can put the class contents just above the test, and later re-organise the code. Test classes extend PHPUnit_Framework_Testcase, and all testing function’s names begin with the word test. These functions are public.
In the functions you make assertions. The most basic is assertTrue(). This function could be used and abused as you want, but mostly useful when a function returns a boolean. Other assert functions lake assertSame(), assertEqual() are more specific and return more meaningful error messages. There are about five or six functions that you’ll use every day. Of course there are many more, you can look up all of them in the official documentation. When you need to set a variable that will be used , call setUp() function at the beginning of the class, the reverse function is tearDown(). Please keep in mind that setUp() will be loaded in each and every test function. Almost all assert functions have a Not version, these do exactly the opposite.
This was a quick introduction to testing in PHP. Of course there is many other things to learn, experiment, but hopefully now you understand why testing is so important in today’s web and test driven development.