3

我正在对一个 URL 提取器进行单元测试,我需要一个总是导致urllib2.urlopen()(Python) 超时的测试 url。我试过制作一个只有 sleep(10000) 的 php 页面,但这会导致 500 内部服务器错误。

每当请求时,我将如何创建导致客户端连接超时的资源?

4

5 回答 5

6

编辑:我看到了 [php] 标记,只是假设这是 PHP 代码——但是,如果这是您正在使用的语言,那么相同的原则可以适用于 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 的特定情况下,如果需要超时结果,只需模拟相关的测试接缝方法以执行本机 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

在 PHP 中,您可以发送带有状态码 408 的标头

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

虽然这里有一些很好的答案,但我发现我只需要一个简单的 php sleep() 调用并覆盖 Apache 的超时。

我知道单元测试应该是孤立的,但是这个端点所在的服务器是无处可去的。

于 2012-10-17T19:26:51.017 回答