8

vfsStream を使用してファイルシステム操作 (実際には php://input からの読み取り) をモックしようとしていますが、適切なドキュメントと例の欠如が本当に妨げになっています。

私がテストしているクラスの関連コードは次のとおりです。

class RequestBody implements iface\request\RequestBody
{
    const
        REQ_PATH    = 'php://input',

    protected
        $requestHandle  = false;

    /**
     * Obtain a handle to the request body
     * 
     * @return resource a file pointer resource on success, or <b>FALSE</b> on error.
     */
    protected function getHandle ()
    {
        if (empty ($this -> requestHandle))
        {
            $this -> requestHandle  = fopen (static::REQ_PATH, 'rb');
        }
        return $this -> requestHandle;
    }
}

PHPUnit テストで使用しているセットアップは次のとおりです。

protected function configureMock ()
{
    $mock   = $this -> getMockBuilder ('\gordian\reefknot\http\request\RequestBody');

    $mock   -> setConstructorArgs (array ($this -> getMock ('\gordian\reefknot\http\iface\Request')))
            -> setMethods (array ('getHandle'));


    return $mock;
}

/**
 * Sets up the fixture, for example, opens a network connection.
 * This method is called before a test is executed.
 */
protected function setUp ()
{
    \vfsStreamWrapper::register();
    \vfsStream::setup ('testReqBody');

    $mock   = $this -> configureMock ();
    $this -> object = $mock -> getMock ();

    $this -> object -> expects ($this -> any ())
                    -> method ('getHandle')
                    -> will ($this -> returnCallback (function () {
                        return fopen ('vfs://testReqBody/data', 'rb');
                    }));
}

実際のテスト (getHandle() を間接的にトリガーするメソッドを呼び出す) では、VFS をセットアップして、次のようにアサーションを実行しようとします。

public function testBodyParsedParsedTrue ()
{
    // Set up virtual data
    $fh     = fopen ('vfs://testReqBody/data', 'w');
    fwrite ($fh, 'test write 42');
    fclose ($fh);
    // Make assertion
    $this -> object -> methodThatTriggersGetHandle ();
    $this -> assertTrue ($this -> object -> methodToBeTested ());
}

これにより、テストがハングするだけです。

明らかに、私はここで非常に間違ったことをしていますが、ドキュメントの状態を考えると、私が何をしようとしているのか理解できません。これは vfsstream によって引き起こされたものですか、それとも phpunit がここで確認する必要があるものを模倣しているのでしょうか?

4

2 に答える 2

9

それで...ストリームでテストする方法は?vfsStream が行うことは、ファイル システム操作用のカスタム ストリーム ラッパーを提供することだけです。単一のストリーム引数の動作を模倣するためだけに、本格的な vfsStream ライブラリは必要ありません。これは正しい解決策ではありません。代わりに、ファイル システム操作をモックしようとしていないため、独自の 1 回限りのストリーム ラッパーを作成して登録する必要があります。

テストする次の単純なクラスがあるとします。

class ClassThatNeedsStream {
    private $bodyStream;
    public function __construct($bodyStream) {
        $this->bodyStream = $bodyStream;
    }
    public function doSomethingWithStream() {
        return stream_get_contents($this->bodyStream);
    }
}

実生活では、次のことを行います。

$phpInput = fopen('php://input', 'r');
new ClassThatNeedsStream($phpInput);

それをテストするために、独自のストリーム ラッパーを作成して、渡すストリームの動作を制御できるようにします。カスタム ストリーム ラッパーは大きなトピックであるため、あまり詳しく説明することはできません。しかし、基本的にプロセスは次のようになります。

  1. カスタム ストリーム ラッパーを作成する
  2. そのストリーム ラッパーを PHP に登録する
  3. 登録されたストリーム ラッパー スキームを使用してリソース ストリームを開く

したがって、カスタム ストリームは次のようになります。

class TestingStreamStub {

    public $context;
    public static $position = 0;
    public static $body = '';

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

    public function stream_read($bytes) {
        $chunk = substr(static::$body, static::$position, $bytes);
        static::$position += strlen($chunk);
        return $chunk;
    }

    public function stream_write($data) {
        return strlen($data);
    }

    public function stream_eof() {
        return static::$position >= strlen(static::$body);
    }

    public function stream_tell() {
        return static::$position;
    }

    public function stream_close() {
        return null;
    }
}

次に、テストケースでこれを行います:

public function testSomething() {
    stream_wrapper_register('streamTest', 'TestingStreamStub');
    TestingStreamStub::$body = 'my custom stream contents';
    $stubStream = fopen('streamTest://whatever', 'r+');

    $myClass = new ClassThatNeedsStream($stubStream);
    $this->assertEquals(
        'my custom stream contents',
        $myClass->doSomethingWithStream()
    );

    stream_wrapper_unregister('streamTest');
}

次に、ストリーム ラッパーで定義した静的プロパティを変更するだけで、ストリームの読み取りから返されるデータを変更できます。または、ベース ストリーム ラッパー クラスを拡張し、代わりに登録して、テスト用のさまざまなシナリオを提供します。

これは非常に基本的な導入部ですが、要点は次のとおりです。実際のファイルシステム操作をモックする場合を除き、vfsStream を使用しないでください。これが目的で設計されています。それ以外の場合は、テスト用のカスタム ストリーム ラッパーを作成します。

PHP は、開始するためのプロトタイプ ストリーム ラッパー クラスを提供します: http://www.php.net/manual/en/class.streamwrapper.php

于 2012-07-28T20:23:02.257 に答える