3

URL fetcher の単体テストを行っていますが、常にurllib2.urlopen()(Python) がタイムアウトするテスト URL が必要です。sleep(10000) だけを含む php ページを作成しようとしましたが、500 内部サーバー エラーが発生します。

要求されるたびにクライアントで接続タイムアウトを引き起こすリソースを作成するにはどうすればよいですか?

4

5 に答える 5

6

編集: [php] タグを見て、これが PHP コードであると想定しました。ただし、使用している言語が Python である場合は、同じ原則が Python でも適用できます。

単体テストを成功させるには、すべての外部の影響から完全に分離してコードの単位をテストする必要があります。これは、テストが正しく機能するためにファイル システム (またはこの場合は外部 Web サーバー) などに依存している場合、それは間違っていることを意味します。テストが外部の Web サーバーに依存している場合、テスト コードが大幅に複雑になるだけでなく、誤検知やその他の誤ったテスト結果が生じる可能性が生じます。

現在のテスト実装では、特定のテスト可能な応答を配信するために本格的なモック Web サーバーが必要なようです。これは当てはまりません。このような広範囲にわたるテストの依存関係は、上で概説した問題につながるだけです。

より良い方法

しかし、ネイティブの PHP 機能とリモート データ (HTTP や FTP など) との相互作用をどのようにテストするのでしょうか? 答えは、テストの「継ぎ目」をコードに追加することです。次の簡単な例を考えてみましょう。

<?php

class UrlRetriever {

    public function retrieve($uri) {
        $response = $this->doRetrieve($uri);
        if (false !== $response) {
            return $response;
        } else {
            throw new RuntimeException(
                'Retrieval failed for ' . $uri
            );
        }
    }

    /**
     * A test seam to allow mocking of `file_get_contents` results
     */
    protected function doRetrieve($uri) {
        // suppress the warning from a failure since we're testing against the
        // return value (FALSE on failure)
        return @file_get_contents($uri); 
    }
}

関連する PHPUnit テストは次のようになります。

<?php

class UrlRetrieverTest extends PHPUnit_Framework_TestCase {

    /**
     * @covers UrlRetriever::retrieve
     * @expectedException RuntimeException
     */
    public function testRetrieveThrowsExceptionOnFailure() {
        $retriever = $this->getMock('UrlRetriever', array('doRetrieve'));
        $retriever->expects($this->once())
                  ->method('doRetrieve')
                  ->will($this->returnValue(false));

        $retriever->retrieve('http://someurl');
    }

    /**
     * @covers UrlRetriever::retrieve
     */
    public function testSomeSpecificOutputIsHandledCorrectly() {
        $expectedValue = 'Some value I want to manipulate';

        $retriever = $this->getMock('UrlRetriever', array('doRetrieve'));
        $retriever->expects($this->once())
                  ->method('doRetrieve')
                  ->will($this->returnValue($expectedValue));

        $response = $retriever->retrieve('http://someurl');
        $this->assertEquals($response, $expectedValue);
    }
}

明らかに、この例は不自然で非常に単純ですが、概念は必要に応じて拡張できます。上記のUrlRetriever::doRetrieve方法のようなテスト シームを作成することにより、標準のテスト フレームワークを使用して結果を簡単にモックすることができます。

この方法により、リモート リソースを操作するネイティブ PHP 関数の複雑な結果をテストすることができます。外部 Web サーバーに触れたり、テスト対象のシステムの外部でエラーの可能性を導入したりする必要はありません。

OP の特定のケースで、タイムアウトの結果が必要な場合は、関連するテスト seam メソッドを単純にモックして、タイムアウトの場合にネイティブ PHP 関数が行うことを何でも実行します。

于 2012-10-05T21:59:06.770 に答える
5

httpbinを試しました/delayか?

httpbin は Python で書かれたHTTP リクエスト & レスポンス サービスです (Kenneth Reitz がモジュールの記述中にモジュールをテストするために開発したと思いますrequests)。ソースはGitHubにあります。/delayリクエスト接続の受け入れが遅れるか、レスポンスの送信が遅れるかは実際にはわかりません。しかし、それがあなたのニーズに正確に合わない場合でも、それを変更したり拡張したりするのは非常に簡単です。

于 2012-10-05T20:33:09.110 に答える
1

接続タイムアウト?たとえば、 を使用しnetcatます。いくつかのポート ( ) でリッスンし、nc -lそのポートからデータをダウンロードしようとしますhttp://localhost:port/.. 接続を開きますが、応答しません。

于 2012-10-05T20:25:00.543 に答える
0

ここにはいくつかの良い答えがありますが、必要なのはApacheのタイムアウトをオーバーライドする単純なphp sleep()呼び出しだけであることがわかりました。

単体テストは分離する必要があることは知っていますが、このエンドポイントがホストされているサーバーはどこにも行きません。

于 2012-10-17T19:26:51.017 に答える
0

PHP では、ステータス コード 408 でヘッダーを送信できます。

header("HTTP/1.0 408 Request Timeout");
于 2012-10-05T20:28:26.100 に答える