2
Help us understand the problem. What are the problem?

posted at

updated at

PHPUnitをマチカネタンホイザにする

ことの発端

↑を見て、『あー、できるかもなー』と思ったのでやってみました。

--printerオプションで出力をカスタマイズできる

PHPUnitには--printerオプションがあります。ドキュメントには

結果を表示するために使うプリンタクラスを指定します。このプリンタクラスは PHPUnit\Util\Printer を継承し、  
かつPHPUnit\Framework\TestListener インターフェイスを実装したものでなければなりません。

と記載があります。独自のプリンタークラスを作ればいけそうです。ソースを追っていくとデフォルトでは、PHPUnit\TextUI\DefaultResultPrinterというクラスを使っているようです。このクラスを拡張したクラスを作ることで、マチカネタンホイザなPHPUnitを作れそうです。

独自プリンタークラス、MatikanetannhauserPrinterを作る

とりあえず、テストに失敗(F)したらむん!、テスト成功(.)したらえい、にしてみたいと思います。上記の通りPHPUnit\TextUI\DefaultResultPrinterの拡張クラスを作りますが、中のコードを見ると、

DefaultResultPrinter.php
    /**
     * A failure occurred.
     */
    public function addFailure(Test $test, AssertionFailedError $e, float $time): void
    {
        $this->writeProgressWithColor('bg-red, fg-white', 'F'); // 失敗した時の出力
        $this->lastTestFailed = true;
    }

    /**
     * A test ended.
     */
    public function endTest(Test $test, float $time): void
    {
        if ($this->debug) {
            $this->write(
                sprintf(
                    "Test '%s' ended\n",
                    \PHPUnit\Util\Test::describeAsString($test)
                )
            );
        }

        if (!$this->lastTestFailed) {
            $this->writeProgress('.'); // 成功した時の出力
        }

        if ($test instanceof TestCase) {
            $this->numAssertions += $test->getNumAssertions();
        } elseif ($test instanceof PhptTestCase) {
            ++$this->numAssertions;
        }

        $this->lastTestFailed = false;

        if ($test instanceof TestCase && !$test->hasExpectationOnOutput()) {
            $this->write($test->getActualOutput());
        }
    }

となっており、addFailureメソッドで失敗時の出力、endTestでテストが成功している時に成功時の出力をしています。このメソッドをオーバーライドしてしまえば、さくっと作れそうです。

tests/MatikanetannhauserPrinter.php
<?php

namespace App\Tests;

use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\TextUI\DefaultResultPrinter;
use Throwable;

class MatikanetannhauserPrinter extends DefaultResultPrinter
{
    /**
     * A failure occurred.
     */
    public function addFailure(Test $test, AssertionFailedError $e, float $time): void
    {
        $this->writeProgress('むん! ');
        $this->lastTestFailed = true;
    }

    /**
     * A test ended.
     */
    public function endTest(Test $test, float $time): void
    {
        if ($this->debug) {
            $this->write(
                sprintf(
                    "Test '%s' ended\n",
                    \PHPUnit\Util\Test::describeAsString($test)
                )
            );
        }

        if (!$this->lastTestFailed) {
            $this->writeProgress('えい、');
        }

        if ($test instanceof TestCase) {
            $this->numAssertions += $test->getNumAssertions();
        } elseif ($test instanceof PhptTestCase) {
            ++$this->numAssertions;
        }

        $this->lastTestFailed = false;

        if ($test instanceof TestCase && !$test->hasExpectationOnOutput()) {
            $this->write($test->getActualOutput());
        }
    }
}

これでプリンタークラスは完成しました。今回、赤文字で失敗表示されるのがちょっと微妙だったので、writeProgressWithColorからwriteProgressに変更しました。なお、composer.jsonのautoload-devにtestsディレクトリがApp\Testsになるようにオートロード設定しています。

プリンターを設定してマチカネタンホイザにする

独自プリンタークラスができたので、vendor/bin/phpunit --printer "App\Tests\MatikanetannhauserPrinter"と実行すればマチカネタンホイザになりますが、毎回プリンター指定はめんどくさいので、設定を変更して独自プリンターを使うように変更します。

phpunit.dist.xml
<?xml version="1.0" encoding="UTF-8"?>

<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         backupGlobals="false"
         colors="true"
         bootstrap="tests/bootstrap.php"
-         convertDeprecationsToExceptions="false">
+         convertDeprecationsToExceptions="false"
+         printerClass="App\Tests\MatikanetannhauserPrinter">
<!-- 以下略 -->

phpunitタグにprinterClass属性を設定することで、デフォルトでこのクラスを使うようになります。

では実行してみましょう。適当にテストクラスを作ります。

tests/SomeTest.php
<?php

namespace App\Tests;

use PHPUnit\Framework\TestCase;

class SomeTest extends TestCase
{
    public function testHoge()
    {
        $this->assertTrue(true);
    }

    public function testFoo()
    {
        $this->assertTrue(true);
    }

    public function testBuzz()
    {
        // むん!にしたいのでわざと失敗させます。
        $this->assertFalse(true);
    }
}

あとは、いつも通りコマンドを叩きます。

% vendor/bin/phpunit
Cannot load Xdebug - it was already loaded
PHPUnit 9.5.16 by Sebastian Bergmann and contributors.

えい、えい、むん!                                                                  3 / 3 (100%)

Time: 00:00.011, Memory: 6.00 MB

There was 1 failure:

1) App\Tests\SomeTest::testBuzz
Failed asserting that true is false.

/Users/user/Project/tests/SomeTest.php:21

FAILURES!
Tests: 3, Assertions: 3, Failures: 1.

はい。できました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
2
Help us understand the problem. What are the problem?