Tag Archives: php

phpunit travis ci undefined offset

or

How I learned to stop hating on PHP

PHPUnit Travis CI Undefined Offset

TL;DR - "Undefined offset" is an E_STRICT level PHP error, and travis-ci.org uses E_STRICT+ by default. Put

  1. error_reporting( E_ALL );
at the top of your phpunit test suite to find and fix all your warnings and notices to fix Travis builds.

pHpaters

Today Laura and I chatted briefly about djangocon presenters bashing PHP. I mentioned that the Promote MDN code is some of my favorite code - in any language. As with my Zend_Rest code, the two keys I've found to writing good PHP code are:

  1. Use a coding standard
  2. Write unit tests

Coding Standard

I use Chris Adams WordPress Coding Standards for PHP_CodeSniffer to stick to that. It was amazing how much nicer the PHP code was when I took just an hour to clean up what I inherited from SEO Smart Links. The PHP community would do well to embrace, encourage, and even enforce coding standards more - the way the Python community does with PEP8.

Unit Tests

I write unit tests for Promote MDN too. My tests/doubles.php is hacky, but it Freaking Works™; I didn't have to build out a set of fixture data or couple the test suite to a running WordPress instance. Like WordPress, I just (ab)use the global namespace to (re-)define dependency functions with doubles.

I also took the opportunity to try out Travis CI. After fixing the travis.yaml and phpunit.xml files just right I got a bunch of test errors with the message "Undefined offset: 1" which frustrated me because I didn't see them locally. While we chatted about PHP, Laura reminded me about PHP's error reporting levels, and she was spot-on. I bumped up to E_ALL locally and got the same errors as Travis.

Error Reporting Level

I spent the next 10 minutes fixing my warnings and notices and realized - I should always use a higher error level in PHP. Python will rightfully gripe at me if I try to access a dictionary element by a key that doesn't exist. But PHP can be equally strict if I set my error level to E_STRICT or higher. So, I'm adding it to my list:

  1. Use a coding standard
  2. Write unit tests
  3. Set error level to E_STRICT

I really think these 3 practices could make any PHP code into good solid code. Instead of simply bashing on PHP, from now on I'm going to simply advise PHP dev's to stick to these practices - it's good for the developers and good for the community.

Unit-testing ZF Controllers without Zend_Test

I've read a couple articles and blog posts recently talking about Zend_Test and/or testing Zend Framework Controllers. Particularly for controller testing, I'm kinda surprised how much plumbing code people are using. I recently started testing some Zend_Controller code (from ZF 1.5 even!) at SourceForge and did not do nearly that much plumbing.

Basically, I want to test the controller code in isolation from the front controller, the router, the dispatcher, the views, etc. All I to do is set up a request object, invoke the action methods of the controllers, and then assert against the variables assigned to the view. For these tests, I don't care about the output of the view templates themselves - I just want to know the controllers are putting the right variables into the view object.

It turns out this is actually pretty simple. I made a custom test case:


class Sfx_Controller_TestCase extends Sfx_TestCase
{
protected $_request;
protected $_response;
protected $_controller;

public function setUp()
{
parent::setUp();

// set up smarty view and restful view helper
$viewRenderer = new Sfx_Controller_Action_Helper_TestViewRenderer();
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

$this->_request = new Zend_Controller_Request_Http();
$this->_response = new Zend_Controller_Response_Cli();

}
}

Sfx_TestCase contains all my bootstrap code. However, the only thing I do in bootstrap is set include path and set up a default db adapter for Zend_Db_Table. I don't do anything with Zend_Controller_Front. So this may as well extend straight from PHPUnit_Framework_TestCase. I'm not sure why others are claiming you have to use Zend_Controller_Front to test ZF Controllers - you don't.

I wrote and use Sfx_Controller_Action_Helper_TestViewRenderer (and proposed it as a core class) to simply create an empty Zend_View object into which the controllers can assign variables. Here's the whole class:


class Sfx_Controller_Action_Helper_TestViewRenderer extends Zend_Controller_Action_Helper_ViewRenderer
{
public function initView()
{
if (null === $this->view) {
$this->setView(new Zend_View());
}
// Register view with action controller (unless already registered)
if ((null !== $this->_actionController) && (null === $this->_actionController->view)) {
$this->_actionController->view = $this->view;
}
}
}

With only this much plumbing, I'm able to test the Controllers in isolation - no worrying about routes, dispatchers, plugins, helpers, nor view templates - like so:


class ProjectControllerTest extends Sfx_Controller_TestCase
{
private function __constructProjectController()
{
return new ProjectController($this->_request, $this->_response);
}

public function test_indexAction_fetches_all_projects()
{
$this->_controller = $this->__constructProjectController();
$this->_controller->indexAction(); // assigns 'resources' to view
$this->assertNotNull($this->_controller->view->resources);
$this->assertEquals(27,count($this->_controller->view->resources));
}

public function test_indexAction_new_since_fetches_only_new_projects()
{
$this->_request->setParam('new_since',1205880839);
$this->_controller = $this->__constructProjectController();
$this->_controller->indexAction();
$projects = $this->_controller->view->resources;
$this->assertEquals(4,count($projects));
foreach($projects as $project){
$this->assertGreaterThan(1205880839, $project->create_time);
}
}

public function test_indexAction_limit_limits_projects()
{
$this->_request->setParam('changed_since', 1205880839);
$this->_request->setParam('order_by','changed_since');
$this->_request->setParam('limit', 5);
$this->_controller = $this->__constructProjectController();
$this->_controller->indexAction();
$projects = $this->_controller->view->resources;
$this->assertEquals(5,count($projects));
$prevChangeTime = 0;
foreach($projects as $project){
$this->assertGreaterThanOrEqual($prevRegTime, $project->change_time);
$prevChangeTime = $project->change_time;
}
}

}

I'm finding this to be a much simpler and easier way of testing ZF Controllers than the other articles I've been reading. Now if you want to test everything in the front controller dispatch process and the view templates, I think Zend_Test is the best bet, but I've not used it yet so I can't be sure. The above classes work fine for what I do.

ZF Rest classes

Holy crap. I forgot that way back after Tulsa Tech Fest I promised to upload some Zend_Controller classes I wrote to enable RESTful behavior. I think the presentation did a decent job conveying their purpose and operation, so here, finally, are the classes themselves.

If anyone does go look at them, feel free to comment/question here on this post about them.

PHP Brasil ’08

I have posted a trip report about PHP Brasil '08 over at the SourceForge.net Community Hub. There's also a video of my talk, a link to Chris Jones's thorough trip report, and links to my presentation slides.

the binary canary testing pattern

I think I just invented a new testing pattern - The Binary Canary.

Basically, I was grouping my PHPUnit tests into a test suite and I realized that my TestCase super-classes were "failing" because they had no tests in them. Obviously this is intentional - only the specific sub-classes would have tests.

I guess I could have made the TestCase super-classes abstract, but instead I added this to the highest-level TestCase class:


/*
* global test plumbing here
*/
class Sfx_TestCase extends PHPUnit_Framework_TestCase
{
public function setUp()
{
// more global test plumbing here
}
public function test_Binary_Canary()
{
$this->assertEquals(
"Binary Canary says test plumbing is working.",
"Binary Canary says test plumbing is working."
);
}
}

My little binary canary serves two purposes:

  1. It adds an "always-pass" test to each of my TestCase classes so they don't throw up any more PHPUnit warnings.
  2. Because my TestCase classes set up context-specific test plumbing, the binary canary test inherited by each of them now alerts me if I screw up any of my test plumbing - and tells me the specific area.

For example:


class Sfx_Db_TestCase extends Sfx_TestCase
{
public function setUp()
{
parent::setUp();
// Db-specific test plumbing
}
}

And:


class Sfx_Controller_TestCase extends Sfx_TestCase
{
public function setUp()
{
parent::setUp();
// Controller-specific test plumbing
}
}

Just like the coal-miner canaries of old, this mechanism gives me a simple yes/no signal as to whether or not my test plumbing will soon kill me, and which plumbing code is the culprit.

Controller Testing in Zend Framework

Ouch, the previous post here was pretty bad. Messy design and it didn't even work correctly. A better guide on the topic is here:

http://tulsaphp.net/?q=node/40