4 min read

Nyancat PHPUnit results

Etienne Marais

PHPUnit has been a handy tool in the developer tool chain for test driven development and code design. There are some pain points that breaks development flow. The insane stack traces when you are expecting an exception and the long scroll when errors occur is enough to demotivate anyone.

Nyancat

A package [https://github.com/whatthejeff/nyancat-phpunit-resultprinter\] exists that cleans up the test results very nicely. Thanks @whatthejeff [https://github.com/whatthejeff\]

Add the package to your development dependencies:

composer require whatthejeff/nyancat-phpunit-resultprinter --dev

Output errors and stack traces

To change the default PHPUnit output you have to extend some of it's classes. This assumes you using Laravel but it's fine for any composer PHP application.

Go ahead and make a Tests\Infrastructure\ namespace.

{
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Tests\\Infrastructure\\": "tests/infrastructure"
        }
    }
}

A new folder inside of tests called infrastructure

Create a new class called ResultPrinter inside a folder called tests/infrastructure/PHPUnit

<?php namespace Tests\Infrastructure\PHPUnit;

use PHPUnit_Framework_TestFailure;

class ResultPrinter extends \NyanCat\PHPUnit\ResultPrinter
{
    /**
     * ResultPrinter constructor.
     * @param null $out
     * @param bool $verbose
     * @param bool $colors
     * @param bool $debug
     */
    public function __construct($out, $verbose, $colors, $debug)
    {
        $debug = false; # Force set debug, set to true for normal phpunit output
        parent::__construct($out, $verbose, $colors, $debug);
    }

    /**
     * @param PHPUnit_Framework_TestFailure $defect
     * @param int $count
     */
    protected function printDefect(PHPUnit_Framework_TestFailure $defect, $count)
    {
        $this->printDefectHeader($defect, $count);
        $this->printDefectTrace($defect);
    }

    /**
     * @param PHPUnit_Framework_TestFailure $defect
     * @param int                           $count
     */
    protected function printDefectHeader(PHPUnit_Framework_TestFailure $defect, $count)
    {
        $this->write(
            sprintf(
                "\n%d) %s\n",
                $count,
                "\033[0;33m" . $defect->getTestName() . "\033[0m"
            )
        );
    }

    /**
     * @param PHPUnit_Framework_TestFailure $defect
     */
    protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect)
    {
        $e = $defect->thrownException();
        $this->writeNewLine();
        $this->write("\033[1;31m" . $e->getMessage() . "\033[0m");
        $this->writeNewLine();

        while ($e = $e->getPrevious()) {
            $this->write("\n\033[0;37mCaused by\033[0m\n"
                . "\033[1;31m" . $e->getMessage() . "\033[0m"
                . "\033[2;31m\n" . $e->getFile() .  " ({$e->getLine()})" . "\033[0m"
            );
            $this->writeNewLine();
        }
    }
}

This class extends the basic functionality of PHPUnit which is handled in the nyan package.

class ResultPrinter extends \NyanCat\PHPUnit\ResultPrinter

I extend the printDefectHeader to show in orange. I extend printDefectTrace to only show the exception messages and not the whole stack trace to display in red.

You can make your own colours and choose what you decide to output. The Green number is the number of all your passed tests. The red number is the total errors/failures. The blue one is the total of skipped/incomplete tests.

You can make your own colours and choose what you decide to output. The Green number is the number of all your passed tests. The red number is the total errors/failures. The blue one is the total of skipped/incomplete tests.

Cat will also show a cute happy face when all your tests are passing.

PHPUnit.xml

To tie this all together you need to add the result printer extensions to your phpunit.xml configuration file.

Specifically these entries:

printerFile="tests/infrastructure/PHPUnit/ResultPrinter.php" printerClass="Tests\Infrastructure\PHPUnit\ResultPrinter"

This is the whole file.

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="bootstrap/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         printerFile="tests/infrastructure/PHPUnit/ResultPrinter.php"
         printerClass="Tests\Infrastructure\PHPUnit\ResultPrinter"
         processIsolation="false"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Application Test Suite">
            <directory suffix="Test.php">./tests</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
            <exclude>
                <file>./app/Http/routes.php</file>
            </exclude>
        </whitelist>
    </filter>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
        <env name="DB_DEFAULT" value="sqlite_testing" />
    </php>
</phpunit>

Now you can run ./vendor/bin/phpunit inside of your application root file and the output will be a nyancat. If you have any problems with files not found run a composer dump-autoload first.


Cover photo by Zbysiu Rodak