Setting up the perfect CodeIgniter & TDD environment
As CodeIgniter has little built-in test support, it’s often difficult setting up a good environment to allow Test-driven Development using SimpleTest and the CodeIgniter framework. I’ve been using Ruby on Rails a lot recently, and I was inspired by the fantastic built in integration with unit, functional and integration tests, so when I returned to CodeIgniter, I delved into the deep dark world of setting up the perfect CodeIgniter and TDD environment.
Directory Structure
The first thing I needed to think about was the directory structure - where were all my test files going to go? I wanted to split up each test type by folder, and so have one folder for Unit tests (to test my models), one folder for Functional tests (to test my controllers) and one folder for my view tests. It made sense to use the MVC-related names (controllers, models, views) as it ties right into the stack, as opposed to the Test-related names (unit, functional, and integration), and it’s slightly more friendly. Obviously, as all my tests are application related, I put them in my /application folder, and then in a tests/ folder.
As for the SimpleTest system, it was far too big to include and convert over to a CodeIgniter library, but was part of the system. I made two directories in CodeIgniter’s /system folder: one for the SimpleTest system itself, called test/ and another to test any CodeIgniter system libraries, called tests/. It might be a bit strange to create a folder for the latter - but I think that if we’re integrating CodeIgniter totally, it makes sense for CodeIgniter to have it’s own test suite. (Heck, I may even fork it and create a total test suite for it and send it to EllisLab).
With all this in mind, I created my three tests/controllers, tests/models and tests/views folders, copied the latest SimpleTest install to system/test, and my directory structure was complete!
Test Runner
The next thing to do was figure out how to run my tests. Now, as tests are a global, application level resource, I wanted it to be at a similar level in the file structure; It needed to reflect an entry point for the application. The best place I found to put it was in a test.php file at my application’s root, right next to my index.php file.
Inside this file I wanted to setup a general test environment, create a global application test suite, load the CodeIgniter environment, handle the loading for the different test types (and their corresponding implementations) and provide a simple GUI for running the tests. Some of this was complicated, some was easy. The tricky bit was to make it as automated as possible - I wanted to hook it right into CodeIgniter so that I could write a test case for, say a model, and then have that model loaded automatically by the test runner. I’ll cover hooking it in with CodeIgniter in a bit, but first, let’s look at setting up the test suite and the SimpleTest specific stuff.
So, inside our test.php test runner we need to we need to create a new Test Suite for our application. It needs to load the files, then create a test suite. We’ll pull a variable from the $_GET array to see what we’re testing. We’ll also check for a ‘test’ variable so that we can run a specific test file. We’ll make a GUI for this later, but it will look something like this:
A pretty hefty bit of code, as I’m sure you can see, but it’s very effective. It allows us to pass ‘controllers’, ‘models’, ‘views’ or ‘all’ in the querystring to run those tests. It’ll then loop through the correct directories, getting a list of files inside them, removing any ., .. and .whatever files (such as Mac OS X’s damned .DS_Store files) and adding the full path to the file name. It’s not checking for the ‘test’ variable yet, so let’s code that now. We also need to remember to actually run the test suite. Start from line 22:
We’re checking to see if there’s a test variable, then just running that if there is. If there isn’t it loops through the directories like previously. Finally, we’re running the tests, and passing them through the HtmlReporter, SimpleTest’s basic HTML-based reporter.
Hooking into CodeIgniter
Next is the hard bit - hooking the testing system into your CodeIgniter install. We need to first set a constant to tell CodeIgniter that it should be in test mode. Then we want to load the CodeIgniter system, but prevent it from outputting anything to the browser. Finally then we can improve our test running code to let our tests use the CodeIgniter system, load the proper controllers etc.
There are a few ways we can go about this. We could use PHP’s output buffering functions to capture all the output from CodeIgniter and throw it away, or we could use our test constant to load a CodeIgniter display_override hook that would ignore the output. We could even load the request into a stream and get rid of the output that way. It’s not a difficult thing to achieve - it’s getting the integration right into CodeIgniter that can prove to be a bit difficult. I’m going to use the first option here - it’s easier and keeps most of the hack-ish code away from your CI app and in the test runner.
Using CodeIgniter’s get_instance() function, we now have the CodeIgniter superclass encapsulated inside the $CI variable. We can then expose that to our tests. Now we have access to the CodeIgniter system, we can make the test runner more intelligent, and get it to load the implementation file by default.
We’ll base this around convention - if you create a home_controller_test.php file in your /application/tests/controllers folder it will load the /application/controllers/home.php implementation file. If you create a /application/tests/models/user_model_test.php file it will try to load the /application/models/user_model.php file. If it can’t find the implementation file, it won’t trigger any errors, and will leave it down to the test file specifically to include it’s implementation file.
A couple of possibly foreign things here - we’re using the ampersand “&” character to pass a reference to the $test object, so it calls the addFile method on exactly the same instance. Oh, and we’re mapping any underscores in the view tests to forward slashes in the implementation (so /application/tests/views/pages_about_view_test.php would map to /application/views/pages/about.php). It might be some good homework to get a recursive directory thing going on in the /application/tests/views directory, so it maps by directory, not underscore. Apart from that, the above code should make sense.
Custom Test Classes
I’d like my tests to automatically have class-level access to the CodeIgniter superobject, and to do this we need to create a custom test class for each type of test. As there are only two types (UnitTestCase for Unit tests) and (WebTestCase for functional and integration) we only have to create two classes that extend those classes, and set the CI superobject in the constructor of those classes. Then we just have to make sure to inherit from them in our tests.
That’s pretty much it for the test runner, we’ll come back in a bit to embed the GUI, but apart from that, our test runner is complete. It’s pretty clever, but there is definitely room for improvement, and there are probably better ways of doing stuff - is you’ve got some code to share, please comment, I’d love to see it. As it stands right now, you’d be able to start using Test-driven Development with an awesome integration system with CodeIgniter - all the next stuff is only if you want to customise it more and make it a little easier to use.
Custom GUI
Let’s create a new file in our system/test directory. We’ll call it custom_test_gui.php, and it’s purpose is to provide a simple HTML form to let us run tests, choose what tests to run and see the results. We’ll make this a basic HTML page which will give us a form, and then we’ll load this file at the bottom of our test runner so that we don’t have to make any drastic changes to the code.
As it’s a simple HTML form, not much explanation is needed. There are five forms embedded into this page, which each post to the current file with a different parameter. Four are buttons, which post to the ‘?all’, ‘?controllers’, ‘?models’ and ‘?views’ switches respectively and the fifth is a text field that takes a specific test file and runs that. Simple, really. Now let’s hop back into our test runner and add the following line right at the bottom of the file:
All in all…
That’s pretty much it. There are plenty more additions that you can make, but for basic purposes, this type of setup works fine. Using this, we can visit test.php in our browser and see our tests being run using the standard HTML reporter (which you can customise a huge amount by creating a custom reporter), change test options with a simple GUI and have the whole system integrated with your CodeIgniter application and system.
As I said earlier, if you’ve written something better, think there’s a better way of going about something or just want to add or change something, please feel free to comment below. It’d be great to read your thoughts and see what changes you’d make. So, go forth and write a CodeIgniter test suite ![]()
Edit: Due to quite a few people’s requests, I’m making the entire test.php file available here. It’s exactly the same as the code that I’ve written above, except it’s all in one file so you can download it as raw on Gist. Like everything else on my blog (with exceptions noted) it’s being released under the Creative Commons Attribution-ShareAlike 3.0 License, so you’re free to use, change and redistribute it as long as you mention my name and make it available under the same license!