7

PhactoryPHPUnitを使用して、PHP Propelプロジェクトのテスト スイートのセットアップに取り組んでいます。現在、外部リクエストを行う関数の単体テストを試みており、そのリクエストのモック応答をスタブしたいと考えています。

テストしようとしているクラスのスニペットを次に示します。

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作成したばかりの$endpointEndpoint オブジェクトを スタブできる でしょうか?

それとも、私はこれについてすべて間違っているのでしょうか。外部 Web 要求に依存する機能を単体テストするためのより良い方法はありますか?

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

助けてくれてありがとう、私は一日中これに夢中でした。ありがとう!!

4

1 に答える 1

12

テストの観点から、コードには次の 2 つの問題があります。

  1. URL はハードコーディングされているため、開発、テスト、または本番用に変更する方法はありません
  2. エンドポイントは、データを取得する方法を知っています。あなたのコードからは、エンドポイントが実際に何をしているのかはわかりませんが、それが低レベルの「Just get me Data」オブジェクトでない場合、データを取得する方法を知る必要はありません。

このようなコードでは、コードをテストする良い方法はありません。リフレクションを操作したり、コードを変更したりできます。このアプローチの問題は、実際のオブジェクトをテストするのではなく、テストで動作するように変更されたリフレクションをテストすることです。

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

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 を呼び出して、戻り値を評価できます。

于 2012-07-02T13:35:36.430 に答える