Building Testable Applications is Hard - The Grumpy Programmer's Guide To Building Testable PHP Applications (2015)

The Grumpy Programmer's Guide To Building Testable PHP Applications (2015)

2. Building Testable Applications is Hard

If you’ve been a PHP developer for any length of time you have probably encountered someone who talks about the benefits of testing your code. Test-Driven Development (TDD), Behaviour-Driven Development (BDD), and Functional Testing are just some strategies you can use. Not testing your code should not be an option.

I was once there too: I did not believe in the value of writing tests for my code. I am familiar with all the excuses people give:

· “Writing tests will slow me down.”

· “Why can’t we just manually test the application in the browser?”

· “I’d be done with this feature already if I didn’t have to write the tests.”

Really, all they are is excuses. Given the tools available to PHP developers there are no reasons to not write tests for your application. Even applications that resist your efforts to refactor individual components to wrap unit tests around them, the dreaded “untestable application”, can be tested in an automated setting.

The reason for investing in automated testing is obvious: any bugs you catch before your application makes it into production cost less in terms of resources (money, developer time) to fix than fixing it in production. In a study that IBM and Microsoft conducted about TDD they found some interesting information. A project using TDD would take 20-40% longer to complete than one that did not use TDD. However, the resulting application contained 40-90% fewer bugs that were discovered in production. If you tweak the statistics just right, how does 20% extra time for 90% fewer bugs sound? How could someone say no to that?

PHPUnit, the gold standard for testing PHP code, is easily integrated into pretty much any project. Install it via PEAR, create a directory in your application for your tests, and start writing them. However, sometimes it’s not that simple. Not every application is already set up for testing.

Do you use a 3rd party system for authenticating users? You’re going to have to figure out how to simulate the responses from that system. You have hard-coded values for speaking to web services? You will have to extract those and put them into a configuration file. The reality is that some applications need refactoring before you can even test them.

In this guide I’m going to show you how you can build an application that is actually testable. Of course we are starting from scratch so it is easier but I will also show alternatives for refactoring code that might not be so test-friendly.

In each chapter we will cover a different topic that will increase your ability to create applications that you can create tests for. Some of the problems you might face are quite easy to solve, while others might require some intrusive changes to make it happen.

In order to illustrate a few of what I consider best practices for creating an application that you can easily test, I will be creating a sample application that utilizes many of the things I am suggesting. The code for the application can be found via my Github account

Our companion application is based on a website for a simulation baseball league. I’ve been in the Internet Baseball League since 1997 and created this version of the site using a now very-outdated version of CakePHP. It certainly gets the job done but I’ve been wanting to clean up stuff going on under the hood and take advantage of some of PHP 5.3’s features.

Rather than duplicate the whole application I’ve extracted those parts of it that generate the main page for the site. It’s probably the most complicated public-facing part of the site. It incorporates the use of multiple models and fairly complicated use of templates. A great candidate to highlight the value of tests to verify functionality.

Even though I have acquired a reputation over the years as being Mister Framework, we’re not going to use a framework here at all. I thought it was important to show you that although most of the major players in the PHP framework world make it easy to write tests for applications built with them, you can wrap just about any application in tests.

I’m using a combination of features and design patterns. While I’m not addicted to design patterns, I’m a believer in the right solution for the right problems Let’s take a look at what we’re using:

· PHP 5.3.8 installed on OS-X 10.7.2 installed using a modified Homebrew recipe

· The Xdebug extension for code-coverage reports

· The APC extension for boosted PHP performance

· Twig for templating

· Postgres as the data store

· Pimple, a dependency injection container that is used as a registry of objects and values needed by other parts of the application

I have also tested this application in an Ubuntu 10.04 (Lucid) virtual machine on my MacBook. More on the role of virtual machines in testing in a later chapter.

For the application architecture I decided to go with a Page Controller structure for each section of the application. If you’ve used a framework you’ve been using an application with a Front Controller structure. This means that every request goes through a single file and then the framework itself interprets the request and determines what controller and action pair needs to be executed.

A Data Mapper pattern as outlined in Jason Sweat’s awesome book on Design Patterns is used to provide access to the database. If you think you might have to change your data store, whether it’s moving to a clustered solution or switching to one of the newer NoSQL options out there, a data mapper will help make that change easier.

In this guide I have only given you a glimpse at the tests that accompany our companion article. Please grab a copy of the code from Github and play around with the application and tests yourself. The code can be downloaded at https://github.com/chartjes/building-testable-applications and I think having the source code will make a lot of the concepts outlined in this guide easier to understand.