The Newbie's Guide to Test-Driven Development

Testing your code is annoying, but the impact of not doing so can be orders of magnitude more annoying! In this article, we'll use test-driven development to write and test our code more effectively.

What is Test-Driven Development?

Since the dawn of the computer era, programmers and bugs have battled for supremacy. It's an inevitable occurrence. Even the greatest programmers fall prey to these anomalies. No code is safe. That's why we do testing. Programmers, at least sane ones, test their code by running it on development machines to make sure it does what it's supposed to.

Test-driven development is a programming technique that requires you to write actual code and automated test code simultaneously. This ensures that you test your code—and enables you to retest your code quickly and easily, since it's automated.

How does it work?

Test-driven development, or TDD as we'll call it from now on, revolves around a short iterative development cycle that goes something like this:

Before writing any code, you must first write an automated test for your code. While writing the automated tests, you must take into account all possible inputs, errors, and outputs. This way, your mind is not clouded by any code that's already been written.

The first time you run your automated test, the test should fail—indicating that the code is not yet ready.

Afterward, you can begin programming. Since there's already an automated test, as long as the code fails it, it means that it's still not ready. The code can be fixed until it passes all assertions.

Once the code passes the test, you can then begin cleaning it up, via refactoring. As long as the code still passes the test, it means that it still works. You no longer have to worry about changes that introduce new bugs.

Great, but how is this better than regular testing?

You felt it was a waste of time to test, since it was only a slight code change?

You felt lazy testing everything again?

You didn't have enough time to test because the project manager wanted it moved up to production ASAP?

You told yourself you'd do it "tomorrow"?

You had to choose between manual testing, or watching the latest episode of your favorite TV show (Big Bang Theory)?

Most of the time, nothing happens, and you successfully move your code to production without any problems. But sometimes, after you've moved to production, everything goes wrong. You're stuck fixing a hundred holes in a sinking ship, with more appearing every minute. You do not want to find yourself in this situation.

TDD was meant to eliminate our excuses. When a program has been developed using TDD, it allows us to make changes and test quickly and efficiently. All we need to do is run the automated tests, and voila! If it passes all automated tests, then we're good to go—if not, then it just means we broke something with the changes. By knowing which exact parts of the test failed, it also allows us to easily pinpoint at which part of the changes it broke, so it makes fixing the bugs easier.

I'm Sold. How Do We Do This?

There's a multitude of PHP automated testing frameworks out there we can use. One of the most widely-used testing frameworks is PHPUnit.

PHPUnit is a great testing framework, which can easily be integrated into your own projects, or other projects built on top of popular PHP frameworks.

For our purposes though, we won't need the multitude of functions that PHPUnit offers. Instead, we'll opt to create our tests using a much easier testing framework, called SimpleTest.

In the next steps, let's assume that we're developing a guestbook application where any user can add and view guestbook entries. Let's assume that the markup has been completed, and that we're simply making a class which contains the application logic of the guestbook, which is where the application inserts and reads to the database. The reading portion of this class is what we're going to develop and test.

Step 1. Set up SimpleTest

Download SimpleTest here, and extract to a folder of your choice -- preferably the folder where you're going to develop your code, or your PHP include_path for easy access.

For this tutorial, I've set up the folder like so:

Index.php will run guestbook.php, and invoke the view method and display the entries. Inside the classes folder is where we'll put the guestbook.php class, and the test folder is where we place the simpletest library.

Step 2. Plan Your Attack

The second step, which is actually the most important one, is to start creating your tests. For this, you really need to plan and think about what your function will do, what possible inputs it will get, and the corresponding outputs it will send. This step resembles playing a game of chess—you need to know everything about your opponent (the program), including all his weaknesses (possible errors) and strengths (what happens if it successfully runs).

So for our guestbook application, let's lay down the schematics:

View

This function will not have any inputs since it will just retrieve all of the entries from the database and send back the data to be printed out.

It will return an array of guestbook records, stating the name of the poster and his message. If there are no records, then it should still return an empty array.

If there are records, the array will have 1 or more values in it.

At the same time, the array will have a specific structure, something like:

Assertions make sure that a certain thing is what it's supposed to be—basically, it ensures that what's returned is what you're expecting it to return. For example, if a function is supposed to return true if it's successful, then in our test, we should assert that the return value is equal to true.

As you can see here, we test the viewing of the guestbook with entries and without. We check if these two scenarios pass our criteria from step two. You probably also noticed that each of our test functions start with the word 'test.' We did this because, when SimpleTest runs this class, it will look for all the functions that start with the word 'test' and run it.

In our test class, we've also used some assertion methods, such as assertTrue, assertIsA, and assertEquals. The assertTrue function checks whether or not a value is true. AssertIsA checks if a variable is of a certain type or class. And lastly, assertEquals checks if a variable is totally equal to a certain value.

Step 4. Fail to Win

Once you're finished writing the code, you should run the test. The first time you run the test, it SHOULD FAIL. If it doesn't, then it means that your test doesn't really test anything.

To run your test, simply run guestbook_test.php in your browser. You should see this first:

This happened because we haven't created our guestbook class yet. To do so, create guestbook.php inside your classes folder. The class should contain the methods we're planning to use, but shouldn't contain anything yet at first. Remember, we're writing the tests first before writing any code.

Step 6. Refactor and Refine Your Code

Since the code we're testing here is pretty simple, our testing and bug fixing didn't last very long. But if this was a more complex application, you'd have to make multiple changes to your code, make it cleaner so it's easier to maintain, and a lot of other things. The problem with this, though, is that change usually introduces additional bugs. This is where our automated test comes in—once we make changes, we can simply run the test again. If it still passes, then it means we didn't break anything. If it fails, we know that we made a mistake. It also informs us where the problem is, and, hopefully, how we'll be able to fix it.

Step 7. Rinse and Repeat

Eventually, when your program requires new functionality, you'll need to write new tests. That's easy! Rinse and repeat the procedures from step two (since your SimpleTest files should already be set up), and start the cycle all over again.

Conclusion

There are a lot more in-depth test-driven development articles out there, and even more functionality to SimpleTest than what was displayed in this article—things like mock objects, stubs, which make it easier to create tests. If you'd like to read more, Wikipedia's test-driven development page should set you on the right path. If you're keen on using SimpleTest as your testing framework, browse the online documentation and be sure to review its other features.

Testing is an integral part of the development cycle, however, it's too often the first thing to be cut when deadlines are imminent. Hopefully, after reading this article, you'll appreciate how helpful it is to invest in test-driven development.

What are your thoughts on Test-Driven Development? Is it something you're interested in implementing, or do you think it's a waste of time? Let me know in the comments!

I'm Nikko Bautista. By day, I work as a software engineer at Bright.com, where we make hiring smarter, faster, and cheaper. By night, I develop web applications and write tutorials for Tuts+.
I was born and raised in the Pearl of the Orient, the Philippines. In 2012, I moved halfway across the world and ended up in the heart of downtown San Francisco, where the culture and energy are unparalleled. It is, without a doubt, the best place to be an engineer.
I specialize in PHP and PHP frameworks. I have experience with Symfony, Zend Framework, CodeIgniter, FuelPHP, and Laravel. I like creating and maintaining developer-friendly APIs. I also have expertise in third-party APIs from Facebook, Twitter, Google, and other platforms. I often explore new technologies, frameworks, and web services by building web applications that use them. Tuts+ allows me to share what I've learned with the world.
In my free time, I love learning and reading about new technologies, innovations and the web. I also enjoy trying new restaurants, taste testing whiskies, taking long walks, and cooking new recipes.