Help us understand the problem. What is going on with this article?

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

PHPUnit でテストが失敗した時に任意のメッセージを表示させたい。
しかも php.iniphpunit.xml をいじらず、手軽に。

phpunit エラー時 メッセージ 任意 表示」で Qiita 記事に絞ってググってみたのですが、タイトルからピンポイントでわかる記事がなかったので、自分のググラビリティとして記事にしました。

TL;DR

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

assertTrueの場合の例
assertTrue(bool $condition[, string $message = ''])
$result = false;
$message = '失敗しちゃった。てへ';
assertTrue($result, $message);

TS;DR

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

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

コマンドの動作例(実装言語は謎)
$ /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 で有名な batsBash Automated Testing System)を使うのが一般的だと思います。

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

ググってみたのですが、タイトルからピンポイントの記事がありません。それっぽい記事を開いても、「"phpunit.xml" に display_errorsdisplay_startup_errors を入れろ」とあったり、「"php.ini" をいじれ」といった記事が大半でした。

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

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

assertTrue()

assertTrue(bool $condition[, string $message = ''])

$conditionfalse の場合にエラー $message を報告します。

実際には、PHPUnit のDocker のコンテナにマウントしていますが、ローカルでもこんな感じです。

ディレクトリ構成
$ 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
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
run_tests.sh
#!/usr/bin/env bash

phpunit --configuration /Regulation/tests/phpunit.xml /Regulation/tests
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);
    }
}
phpunit.xml
<?xml version="1.0" encoding="UTF-8" ?>
<phpunit bootstrap="autoload.php">
    <testsuites>
        <testsuite name="Test suite">
            <directory>tests</directory>
        </testsuite>
    </testsuites>
</phpunit>
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();
    }
}
テストの実行例(わざとテストを失敗させて、メッセージを確認)
$ # テスト実行
$ 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

一応、正しいテストも。

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);
    }
}
テストの実行(今度はパス)
$ 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
動作確認済み環境情報
$ # 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
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした