PHPUnit でエラー時に任意のメッセージをテスト結果に表示させる

> PHPUnit でテストが失敗した時に任意のメッセージを表示させたい。あと `fail()` メソッドで強制的に失敗させた場合など。
>
> しかも `php.ini` や `phpunit.xml` をいじらず、手軽に。

[「`phpunit` `エラー時` `メッセージ` `任意` `表示`」で Qiita 記事に絞ってググって](https://www.google.com/search?q=site:qiita.com+phpunit+%E3%82%A8%E3%83%A9%E3%83%BC%E6%99%82+%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8+%E4%BB%BB%E6%84%8F+%E8%A1%A8%E7%A4%BA)みたのですが、タイトルからピンポイントでわかる記事がなかったので、自分のググラビリティとして。

## TL; DR

> すべてのアサーションの最後の引数は、エラー時のメッセージが指定できるようになっています。

```php:assertTrue()の場合の例
assertTrue(bool $condition[, string $message = ''])
```

```php
class SampleTest extends TestCase
{
    public function testForceFail()
    {
        $result = false;
        $message = '失敗しちゃった。てへ';
        $this->assertTrue($result, $message);
    }
}
```

アサーションではありませんが、わざと失敗にする、つまり強制的にエラーにする `fail()` メソッドにもメッセージが指定できます。

```php:fail()メソッド
fail([string $message = ''])
```

```php
class SampleTest extends TestCase
{
    public function testForceFail()
    {
        if (true) {
            $this->fail('失敗しちゃった。てへ');
        }
    }
}
```

- 参考文献：
    - 「[1. アサーション](https://phpunit.readthedocs.io/ja/latest/assertions.html)」| Latest |  日本語 Docs | PHPUnit @ ReadDocs
    - 「[1. Assertions](https://phpunit.readthedocs.io/en/latest/assertions.html)」| Latest | 英語 Docs | PHPUnit @ ReadDocs
    - 「[DISPLAY A USEFUL MESSAGE WHEN A TEST FAILS](http://www.kreamer.org/phpunit-cookbook/1.0/assertions/display-useful-message-when-test-fails)」@ Kreamer.org
    - [不完全なテスト](https://phpunit.readthedocs.io/ja/latest/incomplete-and-skipped-tests.html?highlight=$this-%3Efail()) | 不完全なテスト・テストの省略 | Latest |  日本語 Docs | PHPUnit @ ReadDocs

## TS; DR

PHP に限らず、Bash、 Python、 Docker などでコツコツと作った「俺様スクリプト集」があります。いずれも `shebang` 付きなので、ファイル名を指定するだけで単体コマンドとして動作します。

イメージとしてはこんな感じのコマンド（スクリプト）です。正常終了時は、実行ステータスを "`0`"、失敗した場合は実行ステータス "`1`" で終了します。

```shell-session:コマンドの動作例（実装言語は謎）
$ /Regulation/REG-000 'Hello World!'
$ echo $?
0
$ /Regulation/REG-000 'Hell World!'
NG (Bad argument. Must be 'Hello World!'. Given: Hell World!)
$ echo $?
1
```

こういった、コマンドの動作テストには Bash の Unit Test で有名な `bats` （[Bash Automated Testing System](https://github.com/bats-core/bats-core)）を使うのが一般的だと思います。

しかし、なぜか PHPUnit を使いたかったのです。しかも、久しぶりの PHP と PHPUnit だったので色々と忘れてました。

[ググってみた](https://www.google.com/search?q=site:qiita.com+phpunit+%E3%82%A8%E3%83%A9%E3%83%BC%E6%99%82+%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8+%E4%BB%BB%E6%84%8F+%E8%A1%A8%E7%A4%BA)のですが、タイトルからピンポイントの記事がありません。それっぽい記事を開いても、「"phpunit.xml" に `display_errors` や `display_startup_errors` を入れろ」とあったり、「"php.ini" をいじれ」といった記事が大半でした。

**PHP のシンタックス・エラーとかでなく、普通にテスト失敗時にオリジナル（任意）のコメントを表示させたい**だけなのです。

「エラー時にメッセージを表示させるだけに、そこまでせんといかんのか」と PHPUnit を使うのを諦めそうになったところ、**普通に PHPUnit 公式のドキュメントに記載がありました**。そして普通に引数に渡せばよかったのでした・・・

> **assertTrue()**
> 
> ```php
> assertTrue(bool $condition[, string $message = ''])
> ```
> `$condition` が `false` の場合にエラー `$message` を報告します。
> 
> - 「[1. アサーション](https://phpunit.readthedocs.io/ja/latest/assertions.html)」| Latest |  日本語 Docs | PHPUnit @ ReadDocs

実際には、[PHPUnit のDocker のコンテナ](https://hub.docker.com/r/phpunit/phpunit)にマウントしていますが、ローカルでもこんな感じです。

```shell-session:ディレクトリ構成
$ tree /Regulation
.
├── REG-000
├── REG-001
├── REG-002
├── REG-003
├── tests
│   ├── REG-000_Test.php
│   ├── REG-001_Test.php
│   ├── REG-002_Test.php
│   ├── REG-003_Test.php
│   ├── autoload.php
│   └── phpunit.xml
└── run_tests.sh
```

```shell-session:REG-000コマンドの動作例（実装言語は謎）
$ /Regulation/REG-000 'Hello World!'
$ echo $?
0
$ /Regulation/REG-000 'Hell World!'
NG (Bad argument. Must be 'Hello World!'. Given: Hell World!)
$ echo $?
1
```

```bash:run_tests.sh
#!/usr/bin/env bash

phpunit --configuration /Regulation/tests/phpunit.xml /Regulation/tests
```

```php:REG-000_Test.php（わざとテストを失敗させて動作確認）
<?php
namespace REG\Tests;

class Reg000Test extends TestCase
{
    public function testReg000()
    {
        $message  = 'Hell World!';
        $lastline = exec("/Regulation/REG-000 '${message}'", $output, $return_var);
        $this->assertTrue($return_var === 0, $lastline);
    }
}
```

```xml:phpunit.xml
<?xml version="1.0" encoding="UTF-8" ?>
<phpunit bootstrap="autoload.php">
    <testsuites>
        <testsuite name="Test suite">
            <directory>tests</directory>
        </testsuite>
    </testsuites>
</phpunit>
```

```php:autoload.php
<?php
namespace REG\Tests;

// これは Docker phpunit/phpunit の場合の autoload 先ですが、
// 適宜自分の composer の autoload.php のパスを指定してください。
require_once('/tmp/vendor/autoload.php');

class TestCase extends \PHPUnit\Framework\TestCase
{
    // Warning も確実にエラーとして扱うようにする
    public function setUp()
    {
        set_error_handler(function ($errno, $errstr, $errfile, $errline) {
            $msg  = 'Error #' . $errno . ': ';
            $msg .= $errstr . " on line " . $errline . " in file " . $errfile;
            throw new RuntimeException($msg);
        });
    }
    public function tearDown()
    {
        restore_error_handler();
    }
}
```

```shell-session:テストの実行例（わざとテストを失敗させて、メッセージを確認）
$ # テスト実行
$ cd /Regulation
$ ./run_tests.sh
PHPUnit 6.5.13 by Sebastian Bergmann, Julien Breux (Docker) and contributors.

F                                                                   1 / 1 (100%)

Time: 250 ms, Memory: 4.00MB

There was 1 failure:

1) REG\Tests\Reg000Test::test_Reg000
NG (Bad argument. Must be 'Hello World!'. Given: Hell World!)
Failed asserting that false is true.

/Regulation/tests/REG-000_Test.php:13

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
$ 
$ echo $?
1
```

一応、正しいテストも。

```php:REG-000_Test.php（今後は正しいテスト）
<?php
namespace REG\Tests;

class Reg001Test extends TestCase
{
    public function testReg000()
    {
        // 正しいデータでのテスト
        $message  = 'Hello World!';
        $lastline = exec("/Regulation/REG-000 '${message}'", $output, $return_var);
        $this->assertTrue($return_var === 0, $lastline);

        // 間違ったデータでのテスト
        $message  = 'Hell World!';
        $lastline = exec("/Regulation/REG-000 '${message}'", $output, $return_var);
        $this->assertTrue($return_var === 1, $lastline);
    }
}
```

```shell-session:テストの実行（今度はパス）
$ cd /Regulation
$ ./run_tests.sh
PHPUnit 6.5.13 by Sebastian Bergmann, Julien Breux (Docker) and contributors.

.                                                                   1 / 1 (100%)

Time: 215 ms, Memory: 4.00MB

OK (1 test, 2 assertions)
$ 
$ echo $?
0
```

```shell-session:動作確認済み環境情報
$ # OS（macOS Mojave）
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.4
BuildVersion:	18E226

$ # PHP バージョン
$ php -v
PHP 7.1.23 (cli) (built: Feb 22 2019 22:08:13) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies

$ # PHPUnit バージョン
$ phpunit --version
PHPUnit 6.5.13 by Sebastian Bergmann, Julien Breux (Docker) and contributors.

$ # Composer バージョン
$ composer --version
Composer version 1.7.2 2018-08-16 16:57:12
```