我应该如何创建一个总是超时的测试资源

Posted

技术标签:

【中文标题】我应该如何创建一个总是超时的测试资源【英文标题】:How should I create a test resource which always times out 【发布时间】:2012-09-27 00:56:36 【问题描述】:

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

如何在客户端请求时创建导致连接超时的资源?

【问题讨论】:

【参考方案1】:

编辑: 我看到了 [php] 标签,并假设这是 PHP 代码—— 但是,如果您使用的是 Python,则同样的原则也适用于 Python。

成功的单元测试要求您在完全隔离不受所有外部影响的情况下测试代码单元。这意味着,如果您的测试依赖于文件系统(或者在这种情况下是一些外部 Web 服务器)之类的东西才能正常运行,那么您做错了。当您的测试依赖于外部 Web 服务器时,您会大大增加测试代码的复杂性,并引入误报和其他错误测试结果的可能性。

听起来当前的测试实现需要一个成熟的模拟 Web 服务器来提供特定的、可测试的响应。 情况不应该如此。这种影响深远的测试依赖只会导致上述问题。

更好的方法

但是如何测试原生 PHP 功能及其与远程数据(如 HTTP 或 FTP)的交互?答案是将 test "seams" 添加到您的代码中。考虑以下简单示例:

<?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 函数在超时事件中会执行的任何操作。

【讨论】:

【参考方案2】:

你试过httpbin的/delay吗?

httpbin 是一个用 Python 编写的 HTTP 请求和响应服务(我认为 Kenneth Reitz 开发它是为了在编写它时测试 requests 模块),源代码在 GitHub。我实际上不确定/delay 是否会延迟接受请求连接或发送响应。但如果它不完全符合您的需求,应该很容易修改或扩展它。

【讨论】:

【参考方案3】:

连接超时?例如,使用netcat。监听某个端口(@98​​7654322@),然后尝试从该端口下载数据..http://localhost:port/。它将打开连接,但永远不会回复。

【讨论】:

哦,你应该完全测试超时。您只是不应该打开一个实际的 URL 并等待超时,就像您在测试其他任何内容时不应该访问网络一样。所有这一切都假设我们谈论的是单元测试,那么在做真实事情的集成测试中也有一些价值。 @delnan,你完全正确。我可能工作过度了,我删除了“单元测试”部分。【参考方案4】:

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

header("HTTP/1.0 408 Request Timeout");

【讨论】:

@dimo414 谢谢 =) 我对 python 一无所知,所以这是我最好的猜测。 这不会导致连接超时。 TCP 连接将立即建立,但 HTTP 响应将是408。该状态意味着服务器断开了客户端为传输 HTTP 请求而打开的连接,因为客户端停止发送的时间过长。这与连接超时完全不同,在这种情况下,客户端尝试建立 TCP 连接以完成 HTTP,但由于服务器没有响应而放弃。【参考方案5】:

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

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

【讨论】:

以上是关于我应该如何创建一个总是超时的测试资源的主要内容,如果未能解决你的问题,请参考以下文章

无论如何都超过了摩卡超时

AWS Lambda 上的 PhantomJS 总是超时

如何计算活动的javascript超时?

如何实现套接字超时?

Jmeter压测软件监控资源问题

ping对方的IP,为啥总是显示“请求超时”?