web-dev-qa-db-ja.com

PHPでcurl呼び出しをユニットテストする方法

Curlの実装を単体テストするにはどうしますか?

  public function get() {
    $ch = curl_init($this->request->getUrl());

    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $result = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
    curl_close($ch);

    if (!strstr($type, 'application/json')) {
      throw new HttpResponseException('JSON response not found');
    }

    return new HttpResponse($code, $result);
  }

返されたコンテンツタイプをテストして、例外をスローできるようにする必要があります。

41
Yader Hernandez

Thomasrutterが提案したように、cURL関数の使用を抽象化するクラスを作成します。

interface HttpRequest
{
    public function setOption($name, $value);
    public function execute();
    public function getInfo($name);
    public function close();
}

class CurlRequest implements HttpRequest
{
    private $handle = null;

    public function __construct($url) {
        $this->handle = curl_init($url);
    }

    public function setOption($name, $value) {
        curl_setopt($this->handle, $name, $value);
    }

    public function execute() {
        return curl_exec($this->handle);
    }

    public function getInfo($name) {
        return curl_getinfo($this->handle, $name);
    }

    public function close() {
        curl_close($this->handle);
    }
}

これで、cURL関数を呼び出さずに、HttpRequestインターフェイスのモックを使用してテストできます。

public function testGetThrowsWhenContentTypeIsNotJson() {
    $http = $this->getMock('HttpRequest');
    $http->expects($this->any())
         ->method('getInfo')
         ->will($this->returnValue('not JSON'));
    $this->setExpectedException('HttpResponseException');
    // create class under test using $http instead of a real CurlRequest
    $fixture = new ClassUnderTest($http);
    $fixture->get();
}

編集簡単な修正PHP解析エラー。

54
David Harkness

Curlを直接使用しないでください。PEARの HTTP_Request2 のようなラッパーを使用してください。これを使用すると、カールドライバーをモックドライバーと交換できます-単体テストに最適です。

7
cweiske

関数モックライブラリを使用する場合があります。私はあなたのために作りました: php-mock-phpunit

namespace foo;

use phpmock\phpunit\PHPMock;

class BuiltinTest extends \PHPUnit_Framework_TestCase
{

    use PHPMock;

    public function testCurl()
    {
        $curl_exec = $this->getFunctionMock(__NAMESPACE__, "curl_exec");
        $curl_exec->expects($this->once())->willReturn("body");

        $ch = curl_init();
        $this->assertEquals("body", curl_exec($ch));
    }
}
6
Markus Malkusch

自分でcURLを使用してクラスをテストしようとしたときに、この質問に遭遇しました。私はDavid Harknessのアドバイスを心に留め、cURLのインターフェースを作成しました。ただし、私の場合、PHPUnitが提供するスタブ/モック機能では不十分だったため、独自のインターフェースのスタブ実装を追加して、すべてGitHubに配置しました。そして、この問題を検索するときにこの質問がGoogleのかなり早い段階で表示されるので、私はここに投稿すると思ったので、他の人が労力を節約できるかもしれません。

こちらです

リポジトリのwiki には、スタブの機能に関するかなり詳細なドキュメントが含まれていますが、ここでは簡単に説明します。

インターフェイスは、PHPのcURL関数の1:1マッピングであり、インターフェイスの使用を非常に簡単に開始できるようにします(ClassUnderTestSAI_CurlInterfaceを実装するインスタンスを渡すだけで、すべてのcURL関数を以前は、そのインスタンスのメソッドとして)。クラスSAI_Curlは、cURLに委譲するだけでこのインターフェースを実装します。 ClassUnderTestをテストする場合は、SAI_CurlStubのインスタンスを指定できます。

スタブは主に、以前の関数呼び出しによってはPHPUnitのモックとスタブがダミーデータを返せないという問題を軽減します(ただし、これはcURLが実際に機能する方法です-オプションを設定し、応答、エラーコード、およびcURL情報はこれらのオプションに依存します)。したがって、ここに短い例を示し、応答の機能を示します(エラーコードとcURL-infoについては、wikiを参照してください)。

public function testGetData()
{
    $curl = new SAI_CurlStub();

    // Set up the CurlStub
    $defaultOptions = array(
        CURLOPT_URL => 'http://www.myserver.com'
    );
    $chromeOptions = array(
        CURLOPT_URL => 'http://www.myserver.com',
        CURLOPT_USERAGENT => 'Chrome/22.0.1207.1'
    );
    $safariOptions = array(
        CURLOPT_URL => 'http://www.myserver.com',
        CURLOPT_USERAGENT => 'Safari/537.1'
    );

    $curl->setResponse('fallback response');
    $curl->setResponse('default response from myserver.com'
                       $defaultOptions);
    $curl->setResponse('response for Chrome from myserver.com',
                       $chromeOptions);
    $curl->setResponse('response for Safari from myserver.com',
                       $safariOptions);

    $cut = new ClassUnderTest($curl);

    // Insert assertions to check whether $cut handles the
    // different responses correctly
    ...
}

応答は、cURLオプションの任意の組み合わせに依存させることができます。もちろん、これをさらに進めることができます。たとえば、ClassUnderTestがサーバーからいくつかのXMLデータを取得して解析したとします(これらのタスクには2つの個別のクラスが必要ですが、この例ではこれを想定しましょう)。その動作をテストするとします。 。 XML応答を手動でダウンロードして、テストでファイルからデータを読み取り、応答に埋め込むことができます。次に、そこにあるデータを正確に把握し、正しく解析されたかどうかを確認できます。または、SAI_CurlInterfaceを実装して、ファイルシステムからすべての応答をすぐに読み込むこともできますが、既存の実装は間違いなく開始点です。

私がこの回答を書いている時点では、@ SAI_CurlStub @はまだcURLマルチライブラリ機能をサポートしていませんが、将来的にはこれも実装する予定です。

このスタブがcURL依存クラスを単体テストしたい人にとって役立つことを願っています。もちろん、クラスを自由にチェックアウトして使用したり、貢献したりしてください。結局、GitHubにあります:)。また、インターフェースとスタブの実装と使用に関して、建設的な批判を受け入れることができます。

3
Martin Ender

これへの1つのアプローチは、使用しているインターフェース(この場合はcurl_関数)を、特定の値を返すダミーバージョン自体に置き換えることです。オブジェクト指向ライブラリを使用している場合、同じメソッド名を持つダミーオブジェクトを置き換えるだけでよいため、これは簡単です(実際、simpletestのようなフレームワークは、ダミーオブジェクトメソッドを簡単にセットアップできます)。それ以外の場合は、おそらく、組み込み関数をダミーでオーバーライドするために使用できる他の魔術がいくつかあります。 この拡張機能には、override_function() が含まれています。これは必要なもののように見えますが、別の依存関係が追加されます。

Curl_関数をダミーバージョンに置き換えずにこれをテストする場合は、特定の結果を返すダミーサーバーを設定して、PHPとそのカール拡張機能が処理する方法をテストできるようにする必要があります。その結果。これを完全にテストするには、PHPがHTTP応答コードなどに依存しているため、ローカルファイルではなく、HTTPを介してこれにアクセスする必要があります。テストには、機能しているHTTPサーバー。

ちなみに PHP 5.4には実際には独自のWebサーバーが含まれます これはこの目的に役立ちます。それ以外の場合は、制御している既知のサーバーにテストスクリプトを配置したり、テストと共に単純なサーバー構成を配布したりできます。

テストのために実際にライブサーバーを使用する場合、テストしているため、これはユニットテストではなく、統合テストになります。 PHPとサーバーの両方、および2つの間の統合。コードが特定のエラーを処理する方法をオンデマンドでテストできることも見逃してしまいます。

2
thomasrutter