2016-12-23 6 views
1

私は、ソケット接続を開いて、使用し、それを閉じたメソッドを持っていました。それをテスト可能にするために、私はコネクションの処理を別々のメソッドに移しました(以下のコードを参照)。PHPで単位テストのためのリソースを偽造するには?

今度はbarIntrefaceMethod()のユニットテストを書いて、メソッドopenConnection()をモックする必要があります。言い換えれば、私は偽物resourceが必要です。

「」などのハンドルを偽造するために、PHPでタイプresourceの変数を手動で作成する方法はありますか?


FooClass

class FooClass 
{ 
    public function barIntrefaceMethod() 
    { 
     $connection = $this->openConnection(); 
     fwrite($connection, 'some data'); 
     $response = ''; 
     while (!feof($connection)) { 
      $response .= fgets($connection, 128); 
     } 
     return $response; 
     $this->closeConnection($connection); 
    } 

    protected function openConnection() 
    { 
     $errno = 0; 
     $errstr = null; 
     $connection = fsockopen($this->host, $this->port, $errno, $errstr); 
     if (!$connection) { 
      // TODO Use a specific exception! 
      throw new Exception('Connection failed!' . ' ' . $errno . ' ' . $errstr); 
     } 
     return $connection; 
    } 

    protected function closeConnection(resource $handle) 
    { 
     return fclose($handle); 
    } 
} 
+0

私はこのことについて正しいことをしているかどうかはわかりません。私が正しく理解していれば、 '$ resource-> openConnection()'から返された擬似/偽のリソースオブジェクトに対して、PHPの組み込みメソッド(fwrite、feofなど)を効果的にテストしたいことがあります。代わりに、それらのメソッドの周りにラッパーオブジェクトを持つことを考えて、 'fwrite($ resource、 'something')'の代わりに '$ resource-> write( 'something')'をテストできるようにする必要があります。 –

+1

コメントありがとうございます!いいえ、確かに、私はネイティブ関数をテストするつもりはありません。そして、はい、接続設定関連のPHP関数をラップするメソッドを作成しました。しかし今、私は、これらのラッピング関数を呼び出す 'barIntrefaceMethod()'のテストのためにその結果を模擬したいと思っています。問題は、私が['resource'](http://php.net/manual/en/language.types.resource.php)を偽造する方法を見つけることができないことだけです。それは 'openConnection()'の嘲笑を挫折させます。 – automatix

+1

ネイティブ関数をテストすることはできませんが、コード内で呼び出す必要があり、実際のリソースハンドルに依存していることは知っています。これを行う最も簡単で優れた設計方法は、ネイティブ関数をラップしたオブジェクトを作成し、その代わりに* *を模倣することです。私はコードでより良い意味を説明する答えを投稿します。 –

答えて

2

リソースモックいくつかの魔法を必要とします。

ほとんどのI/O機能では、protocol wrappersを使用できます。この機能は、ファイルシステムを模擬するためにvfsStreamによって使用されます。残念ながら、fsockopen()などのネットワーク機能は、をサポートしていません。

ただし、この機能を無効にすることはできます。私はRunkitAPDとは考えていませんが、PHPの拡張機能なしで実行できます。

現在の名前空間に同じ名前とカスタム実装を持つ関数を作成します。この技術は別のanswerに記載されています。

また、Go! AOPはほとんどの場合、任意のPHP関数overrideを許可します。 Codeception/AspectMockはこの機能をfunctions mockingに使用します。 Badoo SoftMocksもこの機能を提供します。

この例では、任意の方法でfsockopen()を完全に無効にし、vfsStreamを使用してfgets()fclose()などの入出力機能を呼び出すことができます。 openConnection()メソッドをテストしたくない場合は、それをモックしてvfsStreamをセットアップし、単純なファイルリソースを返すことができます。

3

私のコメントによると、実際のリソースハンドルで実際に呼び出されるネイティブ関数への依存関係を取り除くために、コードを少しリファクタリングする方が良いと思います。 FooClass::barInterfaceMethodのテストで気になるのは、応答を返すということだけです。そのクラスは、リソースが開かれ、閉じられ、書かれているかどうかにかかわらず、それを気にする必要はありません。

あなたの本当のクラス:

class FooClass 
{ 
    public function barInterfaceMethod() 
    { 
     $resource = $this->getResource(); 
     $resource->open($this->host, $this->port); 
     $resource->write('some data'); 

     $response = ''; 
     while (($line = $resource->getLine()) !== false) { 
      $response .= $line; 
     } 

     $resource->close(); 

     return $response; 
    } 

    // This could be refactored to constructor injection 
    // but for simplicity example we will leave at this 
    public function getResource() 
    { 
     return new Resource; 
    } 
} 

このクラスのあなたのテスト:

私はあなたには、いくつかの単純化と非生産擬似コードでは、実証するためのあなたの質問に持っているものに書き換えている下

実際のResourceクラスの場合、ネイティブ関数のラッパーであるメソッドを単体テストする必要はありません。例:

class Resource 
{ 
    // We don't need to unit test this, all it does is call a PHP function 
    public function write($data) 
    { 
     fwrite($this->handle, $data); 
    } 
} 

最後に、あなたがそのままあなたのコードを維持したい場合は、他の選択肢は、あなたが実際にテスト目的のために接続のセットアップテスト・フィクスチャリソースにあります。何かのように

fsockopen($some_test_host, $port); 
fopen($some_test_data_file); 
// etc. 

ここで、$some_test_hostには、テストのために混乱させることができるデータが含まれています。

関連する問題