2012-06-29 36 views
5

私はプロジェクト のテストスイートを、PhactoryPHPUnitを使用して設定しようとしています。私は現在、外部リクエストを行う 関数を単体テストしようとしています。そのリクエストに対して、 というレスポンスをスタブしたいとします。PHPUnitで外部Webリクエストを模擬する方法はありますか?

は、ここで私がテストしようとしているクラスの抜粋です:

class Endpoint { 
    ... 
    public function parseThirdPartyResponse() { 
    $response = $this->fetchUrl("www.example.com/api.xml"); 
    // do stuff and return 
    ... 
    } 

    public function fetchUrl($url) { 
    return file_get_contents($url); 
    } 
    ... 

そして、ここで私が記述しようとしていますテスト機能です。

// my factory, defined in a seperate file 
Phactory::define('endpoint', array('identifier' => 'endpoint_$n'); 

// a test case in my endpoint_test file 
public function testParseThirdPartyResponse() { 
    $phEndpoint = Phactory::create('endpoint', $options); 
    $endpoint = new EndpointQuery()::create()->findPK($phEndpoint->id); 

    $stub = $this->getMock('Endpoint'); 
    $xml = "...<target>test_target</target>..."; // sample response from third party api 

    $stub->expects($this->any()) 
     ->method('fetchUrl') 
     ->will($this->returnValue($xml)); 

    $result = $endpoint->parseThirdPartyResponse(); 
    $this->assertEquals('test_target', $result); 
} 

私は私のテストコードを試した後、私はgetMockとモックオブジェクト を作成し、それを使ったことがないだと、今見ることができます。関数fetchUrl が実際に実行されるので、私は望んでいません。しかし、私はまだ Phactoryを使ってendpointというオブジェクトを作成したいと思っています。それは私の工場の定義から全てのフィールドが であるからです。

既存のオブジェクトにメソッドをスタブする方法はありますか?だからスタンプする fetch_url$endpointエンドポイントオブジェクトを作成しましたか?

またはこれについてはすべて間違っています。ユニットテストに私にとって良い方法がありますか? 外部のWebリクエストに依存する私の関数ですか?

"Stubbing and Mocking Web Services"に関するPHPUnitのドキュメントを読みましたが、そのためのサンプルコードは40行で、独自のwsdlを定義する必要はありません。善良な人々が強く感じない限り、これを処理するには、これが私にとって最も便利な方法だとは信じられません。

何か助けてくれてありがとうございました。私はこの一日中悩んでいます。ありがとう!!

答えて

11

テストの観点からは、あなたのコードは二つの問題ました:URLはあなたの開発のためにそれを変更することのない道を残さず、ハードコードさ

  1. をされ、テストや生産
  2. エンドポイントは、データを取得する方法を知っています。あなたのコードから、エンドポイントが実際に何をしているのかはわかりませんが、低レベルの「Just get me Data」オブジェクトでない場合は、データを取得する方法を知るべきではありません。

このようなコードでは、コードをテストする良い方法はありません。 Reflectionsで作業したり、コードを変更したりできます。このアプローチの問題は、実際のオブジェクトをテストするのではなく、テストで作業するために変更が加えられたことです。

あなたが「良い」のテストを書きたい場合は、あなたのエンドポイントは、このようなものになります。

class Endpoint { 

    private $dataParser; 
    private $endpointUrl; 

    public function __construct($dataParser, $endpointUrl) { 
     $this->dataPartser = $dataParser; 
     $this->endpointUrl = $endpointUrl; 
    } 

    public function parseThirdPartyResponse() { 
     $response = $this->dataPartser->fetchUrl($this->endpointUrl); 
     // ... 
    } 
} 

を今、あなたは何をテストするかに応じて、いくつかのデフォルトの応答を返すDataParserのモックを注入でき。

次の質問です。DataParserをテストするにはどうすればよいですか?主に、あなたはしません。それがPHPの標準関数のラッパーだけであれば、必要はありません。あなたのDataParserは本当にこのように見て、非常に低レベルでなければなりません:

class DataParser { 
    public function fetchUrl($url) { 
     return file_get_contents($url); 
    } 
} 

あなたが必要とするか、またはそれをテストしたい場合、あなたは常に事前に設定を返す、「モック」としてあなたのテストや行為の中に住んでいるWebサービスを作成することができますデータ。あなたは、実際のURLの代わりにこの模擬URLを呼び出し、その戻り値を評価することができます。

+2

これは私がやったことですが、私はそれに興奮していません。 'file_get_contents'をラップする追加のクラスは、私のために過剰なもののように感じます。それは私がそれをテストするためだけに作るだろうコード変更です、そして、それは私には感じられません。私はあなたの最後の段落で説明したことをする[webmock](https://github.com/bblimke/webmock/)の宝石を持っていたルビーから来ています: "あなたのテスト内に住むWebサービスを作成し、 "モック"として機能し、常に事前設定されたデータを返す "。コーディングの代わりに、私は今、モックオブジェクトルートに行きます。思考をありがとう! – goggin13

+2

私はそれがオーバーヘッドだとは思わない。あなたはこのコードを多くの場所で使うつもりです。今から1年後には、file_get_contentsの実装方法が改善され、どこでも変更する必要があります。または、バズに切り替えることをお勧めします(私はこれをお勧めします)。上記のコードでは、オブジェクトを挿入し、1つのメソッド呼び出しを変更する必要があります。小さな例ではオーバーヘッドになるかもしれませんが、アプリケーションが大きくなり、file_get_contentsを再利用したい場合は、それを感謝するかもしれません。 – Sgoettschkes