Pádraic Brady
PHP, Zend Framework and Other Crazy Stuff
PHP, Zend Framework and Other Crazy Stuff
Jan 17th
In the Preamble to this Manifesto, I set out the basic proposal for direct integration of support for applying Behaviour-Driven Development (BDD) to the Zend Framework. The ideal was to move away from line-by-line setup and manipulation of Zend instances towards a more simplified model which is standardised for any PHPSpec specs.
This morning I merged the experimental branch for the implementation of the Zend Framework specific PHPSpec_Context into the main trunk for release in PHPSpec 0.3.0. Documentation of the Zend Context will be completed within the next day or two and added to the growing PHPSpec Manual at http://dev.phpspec.org/manual now available.
The main customisation required to get the Zend Context operational is to set up elements of the FrontController, I’ve selected two primary methods. The first is to use several static methods on PHPSpec_Context_Zend (addControllerDirectory() and addModuleDirectory()) and a more flexible addFrontControllerSetupCallback() which will accept a custom function or static method reference to execute. The former is handy since you can easily organise your bootstrap file into a static class and segregate FrontController, Dispatcher, Action Helper, etc. elements you are using to customise how the Zend Framework operates. Both can be called from any file included into a Context file (e.g. SpecHelper.php).
Here’s a really simple example of a Zend Framework spec. I’ll post a more in-depth tutorial next week once the Manual is finalised, and some final cleanups applied (e.g. allowing for Router specification). The only priority for basic operations left is an implementation of a Zend_Factory class to allow you to replace objects directly instantiated within controllers (to maintain the golden standard of specifying/testing controllers isolated from it’s dependencies). We’ll really start kicking some ZF ass when PHPMock has an initial devel release!
[geshi lang=php]PHPSpec_Context_Zend::addControllerDirectory(‘../src/application/controllers’);
class DescribeTwitterController extends PHPSpec_Context_Zend
{
public function itShouldDisplayHomeTweetsFromDefaultRetrieveAction()
{
$this->get(‘retrieve’);
$this->response()->should->haveText(‘Tweets From Twitterers Followed:’);
}
public function itShouldPostTweetOnSendActionUsingPostData()
{
$this->post(‘send’, array(‘message’=>’@padraicb: All your specs are belong to us’));
$this->response()->should->haveText(‘You submitted a new Tweet!’);
}
public function itShouldRetrieveMessagesForUserIdInParamsFromRetrieveAction()
{
$this->get(‘twitter/retrieve/user/padraicb’);
$this->response()->should->beSuccess();
}
}[/geshi]
It’s obvious this is bare minimum. There is still a lot of ground to cover incl. integrating PHPMock, so you can literally mock/stub the Twitter service package being used in the controller action itself (Note to Matthew – implement Zend_Service_Twitter already…;)) and set expectations of how the Controller should use that Twitter service. Adding some basic HTML/XML safe Matchers would also add some fairly substantial help for specifying what the response output should be, or what template any controller action should render.
But the basics, and in short the most time consuming, bit of integration to get something working with the Zend Framework at a level not requiring any devious workarounds is now complete. The weekend should see some really useful additions to this base. Your comments are most welcome as usual whether here, or on the mailing list.
Jan 11th
Over the course of the next obsessional phase I go through I’ll be attempting to pound the Zend Framework into submission so I can apply Behaviour-Driven Development (BDD) using PHPSpec when I write a Controller. Why? Because I feel like it, and it gives me an excuse to promote one possible incarnation of PHPMock and the PHPSpec Zend Framework extension. See? Perfectly reasonable selfishness!
The other reason is that whether you apply TDD, or the funky new presence of BDD in PHP, applying some form of predictive specification before you rush into PHPEclipse to start typing PHP is a good thing. The alternative is long nights debugging why Controller X and Model Y refuse to produce View Z. The immediate problem with this is that the Zend Framework currently does not offer some means of manipulating Framework operations with a simplified API, instead you have to muck about with Zend_Controller_Front and all sorts of other long-winded-named classes whose operation is often mysterious if you’ve managed to evade tinkering with them while building the Universe’s next greatest social networking site.
Never fear, there are established solutions. One of the easiest to find is archived on the fw-general mailing list for the framework, a relatively straightforward post by the Master Of MVC Operations, Matthew Weier O’Phinney, using PHPUnit and an extended PHPUnit_TestCase class. Here’s the URL in case you missed it:
How to use PHPUnit in a MVC project
For PHPSpec I hope to simplify even further. Moving from Zend_Controller_*::someConfusingMethod() x 5 to PHPSpec_Context::doItAlreadyOrIllKillSomething(). The basis of this theoretical (unless you’ve found the top secret location of the subversion repo) discussion is that simple is better, and easier is way better still. To get there one needs to compact Zend Framework operations into a handful of easy to remember methods and practices. Unfortunately it does also require some Zend Framework fun by establishing a few ground rules on how you write Controller code. We’ll get there much later, because it’s one detail not necessary to cover in an opening sortie and it’s not incredibly troublesome (I think).
To refresh your memory, PHPSpec as a BDD framework is designed to kick your ass into high-TDD gear without needing 2+ years of prior experience to get there. It’s also designed to aid your thought approach by using a readable API. Besides being easy to learn, it also enforces several well-established best practices in TDD and xUnit Patterns. Here’s a quickie shot:
[geshi lang=php]class DescribeZendFramework extends PHPSpec_Context {
public function itShouldMakeWebDevelopmentBetter() {
$zend = new Zend_Framework;
$this->spec($zend->isBetter())->should->beTrue();
}
}[/geshi]
Stored as ZendFrameworkSpec.php, you could run this spec by hitting it’s location on the command line (there is a HTML Runner option) and running:
phpspec ZendFrameworkSpec
Not rocket science. Back to the topic of applying BDD… If one thinks a bit, any ZF request is composed of only a few key components. There’s the Request (POST/GET/COOKIE/URI Params), the Controller, the Action on the Controller, and finally the Response. In between are bits and pieces which while important in a complex application we needn’t dwell on yet.
In a perfect world, we could wave a magic wand and drop big hints like:
[geshi lang=php]class DescribeIndexController extends PHPSpec_Context_Zend
{
public function itShouldDisplayHelloWorldOnIndexAction()
{
$this->get(‘index’);
$this->response()->should->match(“/Hello World/”);
}
}[/geshi]
The basic foundation above is simple enough. The Controller is derived from the class name, the Action is passed to the relevant request method (get() for GET), there’s a response upon which specifications can be defined.
In an inperfect world, it’s not quite as simple as this. In applying TDD or BDD a big annoyance is ensuring we only test isolated specifics. In a typical Controller Action one can easily expect a few things:
1. Models
2. Misc Objects (e.g. Zend_Mail)
3. View Rendering
The biggest problem in there are Models. Models are incredibly irritating objects that insist on writing to databases. They are also as common as dirt in Controllers. Not exactly good company when applying TDD or BDD. The suggested best practice approach is to Mock or Stub these demons out of their evil ways, and turn them into predictable automatons (we can separately spec/test the actual Model classes independently). In other words, we need to Mock/Stub the dependencies of the class being tested/spec’d so it’s isolated – should sound familiar as an intonation of Unit Testing practitioners worldwide.
That’s the problem in a nutshell. Controllers tend to directly instantiate new objects without the benefit of good Dependency Injection. Makes them very difficult to test discretely without falling back on the double-edged sword of pure functional or acceptance testing. And that double-edged sword is quite popular in PHP right now.
Enter something like a Factory. Using a Factory, our specs/tests can potentially intercept objects used in a Controller and replace them with Mocked or Stubbed copies whose behaviour is controller by us. The simplest style would be to use a general object Factory to replace both require_once and new keywords (i.e. introduce a new Controller convention whereby we don’t use the “new” keyword if avoidable in Action code). Something whose API is similar to:
[geshi lang=php]$mailer = Zend_Factory::create(‘Zend_Mail’);[/geshi]
A suitable Zend_Factory class would inherently know the require path for Zend_Mail, and would return a new Zend_Mail object (assume optional constructor params in the API are supported). Couple this with some backend Registry and one could allow PHPSpec/PHPUnit to play god with:
[geshi lang=php]Zend_Factory::replaceClass(‘Zend_Mail’, new Zend_Mail_Mocked);[/geshi]
Now any call to Zend_Factory::create(‘Zend_Mail’) would actually return a registered Mock of Zend_Mail. Granted the API ignores multiple objects and non-Zend classes for now (an API potential discussion for another day if this goes beyond theory) but you get the point. Replacing “new Class” with “Zend_Factory::create(‘Class’)” in a Controller Action would facilitate mocking by introducing a healthy dose of Dependency Injection. Let’s kick another example using PHPSpec and PHPMock (note: PHPMock is undergoing development so the API is questionable as to its stability) where the PHPSpec_Context_Zend extension defines additional helper methods on top of the normal ones attached to PHPSpec_Context. This is suppossed to be the revelationary part of the blog post so pay attention and critique it to death in the comments.
[geshi lang=php]require_once ‘Zend/Mail.php’;
class DescribeIndexController extends PHPSpec_Context_Zend
{
public function before()
{
$this->mail = phpmock(‘Zend_Mail’);
$this->replaceClass(‘Zend_Mail’, $this->mail);
}
public function itShouldSendAnEmailUsingPostData()
{
// setup the Mocked Zend_Mail instance (no real email sent of course)
$this->mail->shouldReceive(‘setBodyText’)->with(‘This was a test email’)->once();
$this->mail->shouldReceive(‘setFrom’)->with(‘anon@example.net’)->once();
$this->mail->shouldReceive(‘setTo’)->with(‘padraic@example.com’)->once();
$this->mail->shouldReceive(‘setSubject’)->with(‘Testing…1…2…3′)->once();
$this->mail->shouldReceive(‘send’)->withNoArgs()->once();
// POST request to IndexController::mailAction()
$this->post(
‘mail’,
array(
‘email’=>’padraic@example.com’,
‘subject’=>’Testing…1…2…3′,
‘body’=>’This was a test email’
)
);
// and because no PHPMock integration with PHPSpec yet…ugly append
$this->spec($this->mail->verify())->should->beTrue();
// we’re ignoring the Response for now..oh ok then
$this->response()->should->beSuccess();
$this->response()->should->match(“/successfully sent/”);
}
}[/geshi]
Screw “new Zend_Mail”, let’s mock the divil ‘imself mo chara! What works for Zend_Mail would feasibly also work for Models. So in theory (at a personal perspective) I like my Manifesto Preamble. Having a simplified approach to applying BDD or TDD to Zend Framework controllers (someone else can take on Symfony if they wish) is something I’d very much like to have.
Now we have no implementation, just a BDD derived specification of what the future implementation should do once complete. To round off the example, here’s a possible implementation:
[geshi lang=php]class IndexController extends Zend_Controller_Action
{
public function mailAction()
{
// ignore security for brevity; normally we’d be damned sure $post
// was the result of filtered/validated data
$post = $_POST;
$mailer = Zend_Factory::create(‘Zend_Mail’); // presto; delivers PHPSpec defined Mock
$mailer->setBodyText($post['body']);
$mailer->setFrom(‘anon@example.net’);
$mailer->setTo($post['email']);
$mailer->setSubject($post['subject']);
$mailer->send();
$this->getResponse()->setBody(‘Email successfully sent.’);
}
}[/geshi]
Of course this is total drivel without a) a stable PHPMock, and b) PHPSpec_Context_Zend. I’ll take a quick proof-of-concept stab later after I investigate the wonders of GIT to see if it kicks SVK up the rear… In the meantime is this drivel for real, or does it have potential? Commentors who use the phrase “Mad Irishman” will be booted…
More when the PHPSpec 0.3.0 branch is active.
Recent Comments