Tuesday, 12 August 2008

PHPUnit - issues with static method calls

Recently ran into a couple of issues concerning stubbing of static method calls when using PHPUnit.

Taking the following code snippet:

  1. <?php
  2. class Example
  3. {
  4. public function methodCall()
  5. {
  6. Logger::logMessage('message', 4, new Exception());
  7. return true;
  8. }
  9. }
  10. ?>


A static method call, which needs to be stubbed out, is made from within the methodCall() function. Mike Lively suggests refactoring under these circumstances, so that calls to static methods that are external to the class are encapsulated within a new class method.

Refactoring, then yields something like this:

  1. <?php
  2. class Example
  3. {
  4. public function methodCall()
  5. {
  6. $this->logMessage('message', 4, new Exception());
  7. return true;
  8. }
  9. public function logMessage($message, $priority, Exception $exception)
  10. {
  11. Logger::logMessage($message, $priority, $exception);
  12. }
  13. }
  14. ?>


A unit test which stubs out the logMessage() function can then be created:

  1. <?php
  2. require_once 'PHPUnit/Framework/TestCase.php';
  3. require_once 'Example.php';
  4. require_once 'Logger.php';
  5.  
  6. class ExampleTest extends PHPUnit_Framework_TestCase
  7. {
  8. public function testMethodCall()
  9. {
  10. $example = $this->getMock('Example', array('logMessage'));
  11. $example->expects($this->any())->method('logMessage');
  12. $this->assertEquals($example->methodCall(), true);
  13. }
  14. }
  15. ?>


At this point, everything works as expected.

However, problems arise if an equivalent process is followed for static method calls. For instance, if the Example class methods are static:

  1. <?php
  2. class Example
  3. {
  4. public static function methodCall()
  5. {
  6. self::logMessage('message', 4, new Exception());
  7. return true;
  8. }
  9. public static function logMessage($message, $priority, Exception $exception)
  10. {
  11. Logger::logMessage($message, $priority, $exception);
  12. }
  13. }
  14. ?>


The unit test now ignores the stubbed method and calls the original class method. Furthermore, direct calls to the stubbed method throw the following error "Fatal error: Using $this when not in object context in......."

These issues have been documented, but were not easy to find and another ticket on the PHPUnit site indicates that stubbing static methods works as expected. As can be seen when reading through the posts on ticket 139, it appears that support for stubbing static methods was withdrawn sometime between Jun 2007 and Feb 2008, with no indication that this will be made available.


Wednesday, 6 August 2008

PHPUnit - installation on OS X under XAMPP

Just a couple of notes on installing PHPUnit under XAMPP for OS X.

Check that pear is in the PATH

sh-3.2# echo $PATH
/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin:/Applications/xampp/xamppfiles/bin



If pear is absent from the PATH, it can be made available by editing /etc/bashrc using vi to add the following lines:

PATH=$PATH:/Applications/xampp/xamppfiles/bin
export PATH



If you're behind a proxy then you'll need to configure pear using something like the following:

pear config-set http_proxy wwwcache.whereveryouare.ac.uk:3128



PHPUnit can then be installed by running the following commands:

pear channel-discover pear.phpunit.de
pear install phpunit/PHPUnit



If necessary, the memory_limit setting in /Applications/xampp/xamppfiles/etc/php.ini can be increased to avoid "Fatal error: Allowed memory size of 8388608 bytes exhausted (tried..." message during installation of PHPUnit

The following message might then be seen when trying to run PHPUnit from the command line:

$ phpunit
Warning: require_once(PHPUnit/Util/Filter.php): failed to open stream: Not a directory in /Applications/xampp/xamppfiles/bin/phpunit on line 40.......



This can be fixed by opening up /Applications/xampp/xamppfiles/etc/php.ini and amending the include_path line added by pear so it looks as follows:

;***** Added by go-pear
include_path=".:/Applications/xampp/xamppfiles/lib/php:/Applications/xampp/xamppfiles/lib/php/pear"
;*****