dimanche 3 juillet 2011

Symfony actions ARE easily testable

All Symfony experts I've met have told me that I'd better not bother with unit testing the actions because it's difficult enough that it just isn't worth it. Even an official Sensio Labs trainer that wisited our company said the same thing (I find that quite surprising). Only when you're doing TDD it's rather annoying not to be able to test the first lines of a new feature as it breaks your flow.

I should never have believed them. Luckily a colleague of mine didn't. In fact it turns out it's strikingly easy so I'd like to share it with you.


Here's an example of a how to unit test a standard action “executeHelloWorld” and an ajax action “executeGoodEvening”. It takes two simple lines in the setUp to prepare instantiation of the action.

<?php
class ActionTest extends PHPUnit_Framework_TestCase {
protected $action;
public function setUp() {
$debug = true;
$configuration = ProjectConfiguration::
getApplicationConfiguration('frontend', 'test', $debug);
$sfContext = sfContext::createInstance($configuration);
$this->action = new myActions($sfContext, null, null);
}
/** @test */
public function iCanTestNormalActionsThroughTheVarHolder() {
$request = new sfWebRequest(new sfEventDispatcher(),
array('name' => 'Brian'));
$this->action->executeHelloWorld($request);
assertThat($this->action->getVar('helloMessage'),
equalTo('Hello Brian'));
}
/** @test */
public function iCanTestAjaxActionsByTheReturnValue() {
$request = new sfWebRequest(new sfEventDispatcher(),
array('name' => 'John'));
$this->action->executeGoodEvening($request);
$eveningGreeting = $this->action->
getResponse()->getContent();
assertThat($eveningGreeting,
containsString('Good evening, John'));
}
}
view raw ActionTest.php hosted with ❤ by GitHub


The rest of the non-magic is in the fact that Symfony actions do not directly return the values we are interrested in. Ordinary (non-ajax) actions do not return anything they just prepare the execution of the template by storing values in a var-holder, we access it by action->getVar('name'). Ajax actions generally directly compiles the partials with parameters passed in an array and adds all that to action->response->content.

Credits go to Marc Nazarian and Sebastien Fromont without whom I still wouldn't TDD my actions.

For reference the action code
<?php
class myActions extends sfActions {
public function initialize($context, $moduleName, $actionName) {
parent::initialize($context, $moduleName, $actionName);
}
public function executeHelloWorld(sfWebRequest $request) {
$this->helloMessage = "Hello ".$request->getParameter('name');
}
public function executeGoodEvening(sfWebRequest $request) {
sfContext::getInstance()->getConfiguration()->loadHelpers('Partial');
$greetingParameters =
array('name' => $request->getParameter('name'));
return $this->renderText(
get_partial('example/goodEvening', $greetingParameters));
}
}
view raw actions.php hosted with ❤ by GitHub

and the partial used
<span>
<?php echo "Good evening, ".$name?>
</span>