B3. Customize listener to match your need

As default PEAR_TestListener SplObserver Interface only listen one event "endTestSuite", we need to define our own version to have ability to listen other events.

In this example we suppose that our class PHP_Reflector have no SplSubject interface defined. So we will use the Event_Dispatcher package to notify additionnal (debug) events : 'startTest', 'endTest' We will see implementation later.

NOTE: In case our class PHP_Reflector got one day a real SplSubject Interface, we have nothing to change here, because we used the subject adapter feature (lines 16-22).

IMPORTANT: Do not forget line 11, otherwise this update method implementation (lines 14-39) won't be called.

PHP code
  1. <?php
  2. require_once 'PEAR/TestListener.php';
  3.  
  4. class PHP_Reflector_TestListener extends PEAR_TestListener
  5. {
  6.     public function __construct(Log_composite $logger, Event_Dispatcher $dispatcher = null)
  7.     {
  8.         /* don't forget to call parent class constructor if you want to attach
  9.            the SplObserver Interface => PEAR_TestListener::update
  10.          */
  11.         parent::__construct($logger, $dispatcher);
  12.     }
  13.  
  14.     public function update($subject)
  15.     {
  16.         try {
  17.             list ($notifyName, $notifyInfo, $notifyObj) = $this->splSubjectAdapter(
  18.                 $subject);
  19.         }
  20.         catch (InvalidArgumentException $e) {
  21.             return;
  22.         }
  23.  
  24.         switch ($notifyName) {
  25.         case 'startTest' :
  26.             $this->logger->log(
  27.                 sprintf("Parsing files %s %s", PHP_EOL, var_export($notifyInfo, true)),
  28.                 PEAR_LOG_DEBUG);
  29.             break;
  30.         case 'endTest' :
  31.             $this->logger->log(
  32.                 sprintf("Parsing results %s %s", PHP_EOL, var_export($notifyInfo, true)),
  33.                 PEAR_LOG_DEBUG);
  34.             break;
  35.         case 'endTestSuite' :
  36.             parent::update($subject);
  37.             break;
  38.         }
  39.     }
  40. }
  41. ?>
generated by Generic Syntax Highlighter - GeSHi

In this test suite launcher, important lines are :

We used also two handlers (file, mail) with custom values.

PHP code
  1. <?php
  2. class PHP_Reflector_TestSuite_Parser extends PHPUnit_Framework_TestCase
  3. {
  4.     public static function main()
  5.     {
  6.         include_once 'PEAR/TestRunner.php';
  7.         include_once 'TestListener.php';
  8.  
  9.         $suite = new PHPUnit_Framework_TestSuite('PHP_Reflector_TestSuite_Parser');
  10.        
  11.         $test_dir  = '/where/ever/you/want';
  12.         $test_dir .= DIRECTORY_SEPARATOR;
  13.  
  14.         $ident     = __CLASS__;
  15.         $level     = PEAR_LOG_INFO;
  16.  
  17.         $conf               = array('timeFormat' => '%Y-%m-%d %H:%M:%S');
  18.         $conf['lineFormat'] = '[%1$s] %2$s: %4$s';
  19.         $conf['append']     = false;
  20.         $filename           = $test_dir . $ident.'_'.date('Ymd').'.log';
  21.  
  22.         $file = Log::singleton('file', $filename, $ident, $conf, $level);
  23.  
  24.         $to   = 'your@example.com';
  25.         $conf = array('subject' => $suite->getName() . ' results',
  26.                       'from' => 'noreply@example.com',
  27.                       'mailBackend' => 'smtp',
  28.                       'mailParams' => array('host' => 'smtp.example.com',
  29.                                             'port' => '25',
  30.                                             'auth' => true,
  31.                                             'username' => 'me',
  32.                                             'password' => 'mystery'
  33.                                            ),
  34.                       'lineFormat' => '%1$s - %4$s'
  35.                      );
  36.         $mail = Log::singleton('mail', $to, $ident, $conf);
  37.         $cond = 'onWarning';
  38.         $mask = PEAR_TestRunner::logMask($cond);
  39.         $mail->setMask($mask);
  40.  
  41.         $logger = Log::factory('composite');
  42.         $logger->addChild($file);
  43.         $logger->addChild($mail);
  44.        
  45.         $listener = new PHP_Reflector_TestListener($logger);
  46.         PEAR_TestRunner::run($suite, $listener);
  47.     }
  48. }
  49. ?>
generated by Generic Syntax Highlighter - GeSHi

PHP code : Alternative solution
  1. <?php
  2. class PHP_Reflector_TestSuite_Parser extends PHPUnit_Framework_TestCase
  3. {
  4.     public static function main()
  5.     {
  6.         include_once 'PEAR/TestRunner.php';
  7.         include_once 'TestListener.php';
  8.  
  9.         $suite = new PHPUnit_Framework_TestSuite('PHP_Reflector_TestSuite_Parser');
  10.  
  11.         $logger   = PEAR_TestRunner::getLogger();
  12.         $listener = new PHP_Reflector_TestListener($logger);
  13.        
  14.         PEAR_TestRunner::run($suite, $listener);
  15.     }
  16. ?>
generated by Generic Syntax Highlighter - GeSHi

For more fine-grained control of which tests to run we can use the --group switch

php path/to/PHP_Reflector/AllTests.php --group parser

PHPUnit 3.3.16 by Sebastian Bergmann.

.............I

Time: 0 seconds

OK, but incomplete or skipped tests!
Tests: 14, Assertions: 27, Incomplete: 1.

Valuable with alternative solution: see code above

php path/to/PHP_Reflector/AllTests.php --configuration path/to/config/file/phpunit.xml --group parser

PHPUnit 3.3.16 by Sebastian Bergmann.

.............I

Time: 0 seconds

OK, but incomplete or skipped tests!
Tests: 14, Assertions: 27, Incomplete: 1.

You will find text file in /where/ever/you/want/PHP_Reflector_TestSuite_Parser_20090603.log, with content as follow.

[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: TestSuite 'PHP_Reflector_TestSuite_Parser' started.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringSimpleClass' started.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringSimpleClass' ended.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringAbstractClass' started.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringAbstractClass' ended.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringInterface' started.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringInterface' ended.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringClassWithMethod' started.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringClassWithMethod' ended.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringClassWithConstant' started.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringClassWithConstant' ended.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringAccessingProperties' started.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringAccessingProperties' ended.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testFilteringClass' started.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testFilteringClass' ended.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testFilteringInterface' started.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testFilteringInterface' ended.
[2009-06-03 21:48:54] PHP_Reflector_TestSuite_Parser: Test 'testFilteringProperty' started.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testFilteringProperty' ended.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testFilteringMethod' started.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testFilteringMethod' ended.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testMethodChaining' started.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testMethodChaining' ended.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testMethodChainingInClass' started.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testMethodChainingInClass' ended.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testProceduralCode' started.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testProceduralCode' ended.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringFunction' started.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringFunction' is incomplete. This test has not been implemented yet.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringFunction' ended.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: TestSuite 'PHP_Reflector_TestSuite_Parser' ended.
[2009-06-03 21:48:55] PHP_Reflector_TestSuite_Parser: TestSuite was successful. Tests: 14, Assertions: 27, Incomplete: 1.

And you will get an email named PHP_Reflector_TestSuite_Parser results with this body

Jun 03 21:48:55 - Test 'testDeclaringFunction' is incomplete. This test has not been implemented yet.
Jun 03 21:48:55 - TestSuite was successful. Tests: 14, Assertions: 27, Incomplete: 1.

FAQ:

Notify additionnal debug information to PEAR_TestListener

Even if our custom TestListener class is ready (PHP_Reflector_TestListener), we have now to implement some notification send at right places.

Have a look inside one test case: for example 'testDeclaringSimpleClass' and especially lines 8,10.

PHP code
  1. <?php
  2.     public function testDeclaringSimpleClass()
  3.     {
  4.         $ds = DIRECTORY_SEPARATOR;
  5.         $fn = dirname(__FILE__) . $ds . 'parseFile' . $ds . 'parser_simpleclass.php';
  6.  
  7.         $files = $this->reflector->addFile($fn);
  8.         $this->dispatcher->post($this, 'startTest', var_export($files, true));
  9.         $this->reflector->parse();
  10.         $this->dispatcher->post($this, 'endTest', $this->reflector->toArray());
  11.  
  12.         $classes =  $this->reflector->getClassNames();
  13.         $exp = array('myClass');
  14.         $this->assertSame($exp, $classes);
  15.     }
  16.  
  17. ?>
generated by Generic Syntax Highlighter - GeSHi

To have a chance to see these new informations on text file, don't forget to change LOG level (see above PHP_Reflector_TestSuite_Parser line 15) accordingly to the one used into PHP_Reflector_TestListener into update method at line 28 and 33. ==> PEAR_LOG_DEBUG

And if you run command below ...

php path/to/PHP_Reflector/AllTests.php --filter testDeclaringSimpleClass

PHPUnit 3.3.16 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 1 assertion)

... you can see the new text file result as follow

[2009-06-03 22:17:44] PHP_Reflector_TestSuite_Parser: TestSuite 'PHP_Reflector_TestSuite_Parser' started.
[2009-06-03 22:17:44] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringSimpleClass' started.
[2009-06-03 22:17:44] PHP_Reflector_TestSuite_Parser: Parsing files
 'array (
  0 => \'C:\\\\wamp\\\\bin\\\\php\\\\php5.2.9-2\\\\includes\\\\PHP\\\\tests\\\\parseFile\\\\parser_simpleclass.php\',
)'
[2009-06-03 22:17:44] PHP_Reflector_TestSuite_Parser: Parsing results
 array (
  'C:\\wamp\\bin\\php\\php5.2.9-2\\includes\\PHP\\tests\\parseFile\\parser_simpleclass.php' =>
  array (
    'extensions' =>
    array (
    ),
    'classes' =>
    array (
      'myClass' =>
      array (
        'attribs' =>
        array (
          'class' =>
          array (
            0 => 'user',
          ),
          'file' => 'C:\\wamp\\bin\\php\\php5.2.9-2\\includes\\PHP\\tests\\parseFile\\parser_simpleclass.php',
          'start' => 2,
          'end' => 3,
          'docblock' => '',
        ),
        'constants' =>
        array (
        ),
        'static_properties' =>
        array (
        ),
        'static_methods' =>
        array (
        ),
        'properties' =>
        array (
        ),
        'methods' =>
        array (
        ),
      ),
    ),
    'functions' =>
    array (
    ),
    'constants' =>
    array (
    ),
    'includes' =>
    array (
    ),
  ),
)
[2009-06-03 22:17:44] PHP_Reflector_TestSuite_Parser: Test 'testDeclaringSimpleClass' ended.
[2009-06-03 22:17:44] PHP_Reflector_TestSuite_Parser: TestSuite 'PHP_Reflector_TestSuite_Parser' ended.
[2009-06-03 22:17:44] PHP_Reflector_TestSuite_Parser: TestSuite was successful. Test: 1, Assertion: 1.