Testing Existing Code

When beginning Unit testing it is hard to find a good starting point. There is a lot of theory and some very good points of reference, but what about actually getting down and dirty & hands-on as so many of us love to do? Here are some ideas, and pointers on how you might want to think about starting out with TDD and Unit testing an existing system. I am a PHP Developer, primarily working on an Apache/MySQL stack, however I would assume these principles can be carried over to any project, if you substitute the appropriate technologies.

I will assume you have been through a few basic tutorials on PHPUnit or are at least familiar with a Unit Testing framework of some description. I won't go into a step by step set up but might assume you know a few things.

1. I would begin by getting a local development copy set up and running. Using XAMPP and MySQL, get a copy of your web application running off of localhost, even if it only has a few test rows of data and is stripped down, or whether it has lots of data isn't really relevant, the fact is I prefer to have a local copy for working on.

2. Obtain your PHP testing framework and get this set up into a directory above your public htdocs folder. I would begin by placing my phpunit files in a folder somewhere not publically accessible, be this above your web directory (htdocs/public_html/www), or within a secure members area or administration area. Not that I intend to have anything secretive in there, just that I feel it's good practice. I would simply create a test class that does the very basic - doesn't even interact with my application and simply asserts that true is true, to verify I have it up and running.

3. Next it is important to understand what unit testing is testing. It is testing single units of your code in isolation. For example, testing a method for verifying the number of items in a shopping basket, should only focus on testing the number of items in the basket, and not be worrying about asserting that the add or update items methods work. (You should have separate tests that verify this, and you should be confident enough in those tests that you should be able to say.. "This works and I can use it in another test". The focus needs to be on the counting method).

4. I would now look to touch on the application ever so slightly. Basically, we want to write an __autoload method, so we don't have to include all our classes for testing everytime. Now the sneaky bit here, is that we are going to override the database connection later, and depending on how this interaction is working, if it's inline then this will be messy, but if it is from a class that handles your database connections and queries (eg some sort of PDO or mysql_ class), this might still get messy.
We want to have an autoload function that will search our application and include the class automatically, so we hope that some form of naming convension is used for helping this. If there are a few different places to search within the file system, ie if some classes may be in a /core/ folder, or if some may be within an individual module, you'll probably want to loop through these folders and check for the class file to include when it is first instanciated within the code. The plan is to add a new location to this list, a place to check before anywhere else, a dummy or testing folder. This will allow us to make a test database wrapper and by calling this the same thing as the live one, it will be included before the autoload method finds the live class, and the tests will use that.

5. If I have lots of files which do'nt rely on the database class, or any class, I will try to leave those for a bit, and find one that does to get me started. I move away from the MySql database, and move towards a file based structure, or in one simple instance I used an in memory php array. I found understanding this was important as when I began instanciating my classes for testing, there was some information being returned from the database or checks being made that certain information was set/not set, and having at least a simple layer of abstration in there to begin with help me get my head around how much the database was being queried.

6. Now its a case of slowly building up the unit tests. For each class, I worked through each method that was there, attempting to do a bit of set up, eg, create a shopping cart. I would then do my assert, eg, assert that cart has no items. Then destroy the cart. The clean up is important to avoid any interference between tests. You want to ensure that each test has a clean start, you should know the state of the system before any test, no matter which tests have ran before. Eg, testing an add item to cart method, should empty and destroy the cart before moving to the next test that counting the number of items present, and you should be aware that it is, as these could interfere if not.

7. While working away through legacy code, I would focus on getting some base of tests for existing classes and models, with a view of going through any code which doesn't utilise classes later. I definitely think having all your model code in classes is the best way towards testing, your controllers and script pages should be as simple as possible to read and follow.

8. Now you have a suite of tests for your code, and can think more about refactoring and rewriting it now. You can make changes to your system in the knowledge that you can run these tests at any time, and know within 10 or 20 seconds if you have broken any of the existing functionality.

Disclaimer: I am not saying this is a fool proof or perfect way to get into testing, but it is the way I have got into it myself. I am also by no means an expert on this subject, and would class myself very novice in the area of testing. However I would get some information out there for anyone new to, or thinking about looking at unit testing.

Comments welcome.

Comments

Popular posts from this blog

Navigating the Jungle of Web Traffic: A Technical Team Lead's Guide to "I'm a Celebrity, Get Me Out of Here"

TCP Handshake over IPv6

The Vital Importance of Secure Wi-Fi Networks