You all should be testing your plugins. You know that, right?

Regardless of whether or not you know that, chances are you have NO EARTHLY IDEA how to accomplish it. I’ve spent a while getting tests up and running for the release of my plugin sfThemeGeneratorPlugin. The truth is, the native testing framework is filled with hacks. This post illustrates how to compound upon these hacks so they do your bidding. We will use the Lime testing framework bundled with symfony.

Methodology

The Why:
First of all, why test your plugins? Plugin tests ensure you won’t break your buddy’s project when you push minor fixes. Also, it gives your buddy this same confidence when upgrading to a new release. Plugins are self contained libraries. They should be able to be tested independently of a particular symfony project. In fact, they should be able to run with any symfony library you provide to them, granted the release is supported by your plugin. A plugin’s test should be independent of every dependency it has. Symfony is a dependent library, as are any plugins required for your plugin to run.

With the ability to define the paths to your plugin’s dependencies, you can quickly test your plugin across multiple releases of those dependencies. It also allows your plugin’s users to run your tests within the context of their application. Who has two thumbs and an awesome plugin? You do my friend. You do.

The How:
Our plugin should contain an executable file to run its tests. The executable should accept parameters for the paths to any of its dependencies. The plugin in my example accepts the symfony_dir argument like this:

$ cd sfMyAwesomePlugin/test/bin/
$ php prove.php
Symfony directory not found.  Please set $_SERVER['SYMFONY'] or provide a --symfony_dir argument
$ php prove.php --symfony_dir=/usr/local/lib/symfony/RELEASE_1_4_0/lib
plugins/sfMyAwesomePlugin/test/functional/sfMyAwesomePluginFunctionalTest..ok
plugins/sfMyAwesomePlugin/test/unit/sfMyAwesomePluginUnitTest........ok
All tests successful. Files=2, Tests=12

At the same time, we want to be able to run our tests as a part of our application test suite. Luckily, this part is feasible through the existing Symfony test framework.

# Task to run all tests in your plugin
$ php symfony test:plugin sfMyAwesomePlugin

The Process

So how do we do it? First, we need to include a symfony project fixture in the plugin. This is required for both unit and functional tests. We’ll add this in test/fixtures/project. Now before you post a picture of Xsibit, let me explain why this isn’t a terrible idea. Even though the symfony framework is rather robust, you can pear it down quite a bit. Take it down to bare bones and you are left with only 6 of files and 10 directories. This is the minimum directory structure needed to run a symfony project fixture:

# symfony project fixture
/path/to/plugin/fixtures/project
    /apps
         /frontend
             /config
                 /frontendConfiguration.class.php
                 /routing.yml
             /lib
                 /myUser.class.php
    /cache
    /config
        /doctrine*
            /schema.yml*
        /databases.yml*
        /ProjectConfiguration.class.php
    /data
        /fresh_test_db.sqlite*
    /lib
    /log
    /web
        /index.php

The files and folders with asterisks(*) next to them are only needed if you’re using a the database layer. For this, I strongly suggest using sqlite. We will copy the fresh database over each time the tests run. Build your test database by running the DB build task in the fixture project (For doctrine and symfony 1.3+ you would use doctrine:build --all).

You can download the symfony project fixture here. Now that we have a project fixture, we need to add our execution binary. We’ll use a prove.php file. This is a simple PHP execution file, and does nothing but include your bootstrap file and run your tests. This is also where we want to check to make sure the path to your symfony library is specified. Add this file to /path/to/plugin/test/bin/prove.php

Next we want to have a Bootstrap class. I took the liberty to make the sfPluginTestBootstrap class. I refuse to write a single line of PHP procedural code that is not strictly necessary. This file will determine the symfony directory, autoload symfony core, and register the setup and teardown functions. This class also begs you to extend it for your project if needed. Add this file to /path/to/plugin/test/bootstrap/sfPluginTestBootstrap.class.php

Finally, we need to have a bootstrap file. This is similar to the bootstrap you’ll find in any of your project’s tests. While I am no more a fan of this than you, this is the way things work with Lime. Mine looks like this: bootstrap.php. This will be the sole include at the top of each test. This will allow you to run each test manually via command line ($php sfMyAwesomePluginTest.php) and via the prove.php file. Wootness. Add this file to /path/to/plugin/test/bootstrap/bootstrap.php

The Result

When you get to this point, you should have something like this in your plugin’s test directory: Sample Plugin Test Directory

So what do we need now? The good news is… that’s pretty much it! Now we just need to add the test files themselves. These will look exactly like a regular symfony test. That’s right, you can write a unit or functional test in whatever project you’re using, and just move the files into your plugin. You may need to adjust the bootstrap include at the top of each test, but this approach follows the symfony convention, so even that is unlikely. That’s nice, right?

Now sit back and enjoy all the benefits that come with having beautiful test coverage. Confidently notate compatible symfony versions. Give your users the piece of mind knowing your plugin is compatible with their application. And refactor with confidence! I basically had to cage fight a rabid bear to figure all this out. Now all you need to do is follow these steps. Give my wounds purpose, and test your plugins.

Fantasy: Your client has an incredibly simple, intuitive, and cohesive ACL schema in mind. Permission and group names make sense, never change, and current users perpetually encounter properly restricted behavior. While we’re at it, you’re also able to code one-handed while scuba-diving the Caymans.

The Cold Hard Truth: Permission names are inconsistent, Groups are changed and reassigned, and your poor users are left dangling somewhere between “Why can I see the administrator’s Social Security Number?” and “The ‘Donate Large Sums of Money’ page is giving me permission denied!”

I created a simple solution to this problem with a few new symfony tasks now available via csSecurityTaskExtraPlugin. In a nutshell, the plugin allows you to more easily visualize the security coverage of your application. Here are some examples below:

$ ./symfony app:security frontend

App Security

The app:route-security task compares your security.ymls to all the routes in your application

$ ./symfony app:route-security frontend

Route Security

You can also list who has access to which actions specified in security.yml with the group-security task.

 $ ./symfony app:group-security frontend

Group Security

Pass the name of an sfGuardGroup object as the second argument to narrow down your output

 $ ./symfony app:group-security frontend author

Group Security

List users who has access with the user-security task.

 $ ./symfony app:user-security frontend

User Security

Pass the username or id of an sfGuardUser object as the second argument to narrow down your output

$ ./symfony app:group-security frontend andyadministrator
OR
$ ./symfony app:group-security frontend 3

User Security

It’s fairly basic right now. The product of a few hours’ work and a desire to get something new out into the community. What other enhancements would you like to see to give you more/better control of your site’s security coverage?

What a great week! So many things have been going on. If you live in Nashville especially, you have a lot to be excited about. The official releases of symfony 1.3 and 1.4 provide a lot of exciting new functionality in the framework, which you can read about here. But even more exciting is this year’s advent calendar, a large part of with being contributed by Nashville’s own Ryan Weaver! Be sure to check it out, and consider purchasing the book from amazon to support the community.

Also, my personal project Symplist is now launched, and in Alpha. Please check it out and provide feedback. Symplist is a plugin site that exists for the community. Its greatest asset will come with individuals like you rating and commenting on plugins. Another section of the site, which I’ve dubbed “Community Lists”, is something I hope will be a great help to the community. This section will function as a repository for dense information. An example of this is a list I’m putting together of Symfony Best Practices. The highest-rated items in each list sort to the top, as do the lists themselves. Check them out, rate and add items, and leave some feedback!

On a separate note, Jon Wage (Nashville native) has recently been pushing PHP Interoperability Standards with PHP 5.3′s namespacing support. I would recommend checking it out! If you personally have any PHP projects, or are currently developing one, consider incorporating these standards.

If you want to get involved, consider joining the Nashville Symfony User’s Group, which meets at Centre{source} the first Tuesday of every month! Symfony is taking off, and Nashville is right on board!

If you are wondering if I develop anything other than Symfony plugins,  in the past week I have worked on projects in Rails, Drupal, WordPress, and Cocoa/Objective C. I hope to post more on these later, as they become accessible in some form or another.

Your comments and support would be much appreciated, however, on the three plugins I have added/heavily updated in the past two weeks. They are the following:

  1. csNavigationPlugin – Quite possibly my best plugin yet (and coincidentally that with the least users). I have worked on this for months, and recently re-factored it into a production-ready state.
  2. csSettingsPlugin – Initially a port of Chris Wage’s sfDoctrineSettingsPlugin, this plugin is now almost entirely refactored. Supports great settings-integration in your project.
  3. csDoctrineSlideshowPlugin – This plugin has been heavily refactored in order to allow for the use of multiple slideshow libraries. Currently supporting JQuery Cycle and Google Slideshow2, the csDoctrineSlideshowPlugin can easily toggle between any supported library.

In exploring the plugins offered for Rails, Drupal, and WordPress, I am able to obtain a bearing on how these plugins compare. The plugins offered for Symfony are incredibly far behind the curve, and it would be an honor for me to help change this. I can’t do that without feedback, however. Thanks for your support.