PHPUnit で標準入力のテストをしたい（STDIN を使った関数のテスト・サンプル）

> **標準入力を取得するユーザー関数やメソッドを作りたい場合のテスト**をしたい。

```php:問題の関数
function getContentsFromStdin()
{
    $contents = \file_get_contents('php://stdin');

    if ($contents === false) {
        throw new \RuntimeException('Failed to read contents from STDIN.');
    }

    return $contents;
}

```

[ここ１年以内に作成 or 更新された Qiita 記事で「phpunit 標準入力 テスト」に絞ってググって](https://www.google.com/search?q=site:qiita.com+phpunit+%E6%A8%99%E6%BA%96%E5%85%A5%E5%8A%9B+%E3%83%86%E3%82%B9%E3%83%88&tbs=qdr:y)も、ピンポイントに欲しい記事がなかったので自分のググラビリティとして。

## TL; DR

> `php` [ストリーム](https://www.php.net/manual/ja/intro.stream.php) にラッパーを登録し、`php://STDIN` にテストデータを書き込んでテストする。

- PHP 7.0.33 〜 7.4.5 + PHPUnit ^9 | ^7.5 | ^6.5 で動作確認したサンプルを GitHub にあげました。（Dockerfile あり）
  - https://github.com/KEINOS/Practice_PHPUnit-test-of-STDIN @ GitHub

## TS; DR

### テストしたい対象の関数

```php
<?php
declare(strict_types=1);

namespace KEINOS\Sample;

function getContentsFromStdin()
{
    $contents = \file_get_contents('php://stdin');
    if ($contents === false) {
        throw new \RuntimeException('Failed to read contents from STDIN.');
    }

    return $contents;
}

```

### テストの内容

```php:getContentsFromStdinFunctionTest.php
<?php
declare(strict_types=1);

namespace KEINOS\Tests;

final class FunctionGetContentsFromStdinTest extends \PHPUnit\Framework\TestCase
{
    public function testRegularInput()
    {
        $result_expect = 'World!';

        // ラッパー "MockPhpStream" を "php://" ストリームに登録する
        $existed = in_array('php', \stream_get_wrappers());
        if ($existed) {
            \stream_wrapper_unregister("php");
        }
        \stream_wrapper_register("php", '\\KEINOS\Tests\MockPhpStream');

        // "STDIN" にデータを書き込む
        \file_put_contents('php://stdin', $result_expect);

        // テストしたい関数（getContentsFromStdin()）からデータを取得
        $result_actual = \KEINOS\Sample\getContentsFromStdin();

        // ラッパーの解放
        \stream_wrapper_restore("php");

        // アサーションの実行
        $this->assertSame($result_expect, $result_actual);
    }
}

```

### テストに使うラッパー・スクリプト "MockPhpStream"

```php:MockPhpStream.php
<?php
declare(strict_types=1);

namespace KEINOS\Tests;

/**
 * REF: http://news-from-the-basement.blogspot.com/2011/07/mocking-phpinput.html
 */
class MockPhpStream
{
    protected $index  = 0;
    protected $length = null;
    protected $data   = 'hello world';

    public $context;

    public function __construct()
    {
        if (file_exists($this->buffer_filename())) {
            $this->data = file_get_contents($this->buffer_filename());
        } else {
            $this->data = '';
        }
        $this->index  = 0;
        $this->length = strlen($this->data);
    }

    protected function buffer_filename()
    {
        return sys_get_temp_dir(). DIRECTORY_SEPARATOR . 'php_input.txt';
    }

    public function stream_open($path, $mode, $options, &$opened_path)
    {
        return true;
    }

    public function stream_close()
    {
    }

    public function stream_stat()
    {
        return array();
    }

    public function stream_flush()
    {
        return true;
    }

    public function stream_read($count)
    {
        if (is_null($this->length) === true) {
            $this->length = strlen($this->data);
        }
        $length = min($count, $this->length - $this->index);
        $data = substr($this->data, $this->index);
        $this->index = $this->index + $length;

        return $data;
    }

    public function stream_eof()
    {
        return ($this->index >= $this->length ? true : false);
    }

    public function stream_write($data)
    {
        return file_put_contents($this->buffer_filename(), $data);
    }

    public function unlink()
    {
        if (file_exists($this->buffer_filename())) {
            unlink($this->buffer_filename());
        }
        $this->data   = '';
        $this->index  = 0;
        $this->length = 0;
    }
}

```

## 参考文献

- [Mocking php://input](http://news-from-the-basement.blogspot.com/2011/07/mocking-phpinput.html) @ News from the basement
- [コメント](https://qiita.com/chanchanrk/items/f6f61bb24c194c048275#comment-3c7b5bb6aaaf11379ae1) | PHPで標準入出力をテストする（改め） @ Qiita