How to make your own printer class for PHPUnit.

Long time ago PHPUnit allows us to create our own printer class also identified as ResultPrinter.

Probably many developers have already created their own version corresponding to their need, and look and feel.

At least few examples :

Today, I’ll propose to learn how avoid reinvented the wheel, but reuses the standard PSR-3 logging concept.

You’ll need :

What is the Bartlett phpunit-LoggerTestListener package ?

It allow to implement the PHPUnit TestListener feature with any PSR-3 loggers.

Whatever you used your own private version (compatible PSR-3) or the famous Monolog, you may log all events occured during a test run.

Lets see how we can use it with a simple test suite described as follow:

<?php

namespace Your\Name_Space;

class YourTestSuite extends \PHPUnit_Framework_TestCase
{
    public function testIncomplete()
    {
        // Optional: Test anything here, if you want.
        $this->assertTrue(TRUE, 'This should already work.');

        // Stop here and mark this test as incomplete.
        $this->markTestIncomplete(
          'This test has not been implemented yet.'
        );
    }

    public function testRisky()
    {
    }

    public function testSkipped()
    {
        $this->markTestSkipped('This test was skipped for any reason.');
    }

    public function testFailure()
    {
        $this->assertEmpty(array('foo'));
    }

    public function testPass()
    {
        $this->assertTrue(TRUE, 'This should always work.');
    }

    /**
     * @dataProvider additionProvider
     */
    public function testDataProvider($a, $b, $expected)
    {
        $this->assertEquals($expected, $a + $b);
    }

    public function additionProvider()
    {
        return array(
            array(0, 0, 0),
            array(0, 1, 1),
            array(1, 0, 1),
            array(1, 1, 3)
        );
    }
}

Basic usage is to declare as a standard phpunit test listener in a phpunit.xml config file.

<?xml version="1.0" encoding="UTF-8"?>
<phpunit
    backupGlobals="true"
    backupStaticAttributes="false"
    bootstrap="bootstrap.psr3.php"
    colors="false"
    stopOnError="false"
    stopOnFailure="false"
    stopOnIncomplete="false"
    stopOnRisky="false"
    stopOnSkipped="false"
    beStrictAboutTestsThatDoNotTestAnything="true"
    verbose="false"
>
    <listeners>
        <listener class="Bartlett\LoggerTestListener">
            <arguments>
                <object class="YourPsr3Logger">
                    <arguments>
                        <string>YourCustomChannel</string>
                        <string>debug</string>
                    </arguments>
                </object>
            </arguments>
        </listener>
    </listeners>
    <testsuites>
        <testsuite name="Demo Test Suite">
            <file>testSuite.php</file>
        </testsuite>
    </testsuites>
</phpunit>

Where YourPsr3Logger is your logger compatible PSR-3.

Logging PHPUnit events is the base concept that you should have understood before to continue.

Creating our own ResultPrinter

In summary, our ResultPrinter is no more no less than just a logger that will process and reformat log records received.

Avoid following trap
  • your ResultPrinter MUST always extend the PHPUnit_TextUI_ResultPrinter class

  • do not build your ResultPrinter with signature class ResultPrinter extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener

Even if the previous point is possible, you can’t reuse easily the PHPUnit context (--verbose, --debug, --colors). See issue https://github.com/sebastianbergmann/phpunit/issues/1674

Our goal, at this point is to build a ResultPrinter class that may be used with a standard PSR-3 logger (example) and a Monolog logger instance (example).

Don’t misunderstand with two examples that have different targets.

  • standard PSR-3 logger will render log records only to a single target (the CLI console)

  • monolog logger will render log records both to the CLI console and save them in a local file.

In this situation, and to easily switch between two loggers, our ResultPrinter class MUST respect the same interface.

<?php

use Bartlett\LoggerTestListenerTrait;

use Psr\Log\LoggerAwareTrait;
use Psr\Log\LogLevel;

class ResultPrinter extends \PHPUnit_TextUI_ResultPrinter
{
    use LoggerTestListenerTrait, LoggerAwareTrait;

    public function __construct($out = null, $verbose = false, $colors = self::COLOR_DEFAULT, $debug = false, $numberOfColumns = 80)
    {
    }

    public function printResult(\PHPUnit_Framework_TestResult $result)
    {
    }

    protected function printHeader()
    {
    }

    public function messageProcessor(array $record)
    {
    }

    public function suiteNameProcessor(array $record)
    {
    }
}
  • The LoggerTestListenerTrait allow to reuse to PHPUnit test listener feature, we have seen previously.

  • The LoggerAwareTrait allow to inject an instance of the logger to log all PHPUnit events.

  • The printHeader() method MUST be compatible with base PHPUnit_TextUI_ResultPrinter class.

  • The LoggerTestListenerTrait provide a printFooter() method compatible PHPUnit_TextUI_ResultPrinter.

  • The messageProcessor() method is a log record processor (in Monolog concept) that is responsible to reformat message for the CLI console.

  • The suiteNameProcessor() method is another log record processor (in Monolog concept) that is responsible to rename the suite name (for php-compatinfo references).

Your are of course, free to add more specialized processors depending of your need, and console render you want.

To finish

do not forget to use either the printerClass attribute in your phpunit.xml file or the --printer switch.

Here are some previews you can get with source code provided on the php-compatinfo project / generictest branch.

$ phpunit --printer Bartlett\Tests\CompatInfo\ResultPrinter --colors tests\ClassIssueTest.php
phpunit-printer-sample1.png
--verbose and --debug are available and will change output a little.
--verbose print more details when tests do not PASS for any reasons.
--debug print more details than the --verbose, especially the startTest and endTest PHPUnit events.
$ phpunit --printer Bartlett\Tests\CompatInfo\ResultPrinter --colors --debug tests\ClassIssueTest.php
phpunit-printer-sample1-debug.png

You are free and encourage to write your own processors to change render that will match your feeling !

May the 4th be with you

Published by Laurent Laville on 2015-05-04