kshabazz/interception

此包已被废弃,不再维护。未建议替代包。

一个PHP库,充当流包装器函数的中介,例如fopen和fsockopen;用于记录网络请求并回放。

0.4.1 2015-12-17 15:24 UTC

This package is auto-updated.

Last update: 2022-06-14 15:52:39 UTC


README

  • 简介
  • 要求
  • 如何工作
  • 示例
    • 如何在单元测试期间保存HTTP请求以供回放(手动方式)
    • 使用PHPUnit中的InterceptionListener设置拦截单元测试注解

简介

该库的目的是记录URI资源请求(1)并从本地缓存中回放。当注册拦截流包装器时,对于给定的协议,它将处理该协议的所有URI请求。作为中介,第一个请求将被允许以便可以保存到本地;然后此保存将回放给后续的所有调用。请注意,回放是无限的,直到删除本地缓存或注销拦截流包装器。

该库的预期用途是在单元测试运行期间帮助模拟/模拟HTTP请求。通过在单元测试期间保存并回放对服务的真实请求。由于它在PHP流级别工作,现有代码只需进行最小更改,如果有的话。

除了fopen和file_get_contents之外,还有一个Guzzle处理器,可以用于使代码与使用Guzzle的代码一起工作。请参阅使用(Guzzle ~5.0)[#how-can-i-use-this-with-guzzle]的示例。

免责声明

此库仅与PHP流一起工作。其他PHP扩展,如cURL,不受支持。

  1. 请求 - 包含头和有效负载,保存为*.rsd
  2. rsd - 代表“原始套接字数据”文件。不进行编码。

要求

  • PHP ~5.4
  • PHPUnit ~4.7

如何工作

首先注销内置的HTTP/HTTPS协议包装器,然后将其替换为Interception StreamWrappers\Http包装器。一旦注册,您必须使用Http::setSaveFilename()指定一个文件名,该文件名将用于将响应保存到具有该名称的文件。使用PHP流函数fopen()或file_get_contents()请求HTTP/HTTPS请求时,拦截包装器将然后建立TCP连接并返回该资源给这些函数。一旦在资源上调用eof(),将使用提供的名称保存一个文件;其中将包含头和检索到的任何内容。

关于fopen()的说明。与file_get_contents()不同,不是一次性读取所有内容。只有使用(fread on)返回的TCP资源读取的内容会在fclose()调用时保存到文件中。

一旦设置文件名,所有HTTP请求都将将该文件中的响应保存。因此,如果您想有不同的请求,您需要提供一个新文件名。或者,您可以调用stream_wrapper_restore('http')来恢复默认的Http功能,这将删除拦截流包装器。

现在您已经保存了对请求的响应,可以使用拦截 Http 流包装类(StreamWrappers\Http)来模拟请求。只要将 StreamWrappers\Http 类注册为 PHP 流包装处理程序,就无需执行其他任何操作。由于这是在流包装层,因此它适用于任何使用 PHP Streams 的代码。

在这是对 URL 的第一次调用的情况下,使用 \fopen,但 \foef 没有返回 TRUE;则只会保存部分内容。至少将保存请求的标题。

如果有两个或更多请求使用 \fopen 或 \file_get_contents 对同一 URL 进行,并且在任何请求关闭之前;它们都将具有独立的 TCP 资源。它们将在 \fclose 时各自保存一个文件,后续的保存将覆盖之前的保存。

示例

如何在单元测试期间保存 HTTP 请求进行回放(手动方式)

use \Kshabazz\Interception\StreamWrappers\Http;

class HttpTest extends \PHPUnit_Framework_TestCase
{
    static public function setUpBeforeClass()
    {
        // Unregister the built-in PHP HTTP protocol stream wrapper.
        \stream_wrapper_unregister( 'http' );

        // Pick a place where we want to save request for playback.
        Http::setSaveDir( './fixtures/' );

        // Register the Interception stream wrapper for the HTTP protocol.
        \stream_register_wrapper(
            'http',
            '\\Kshabazz\\Interception\\StreamWrappers\\Http',
            \STREAM_IS_URL
        );
    }

    /**
     * Make sure we restore the original HTTP stream wrapper for the test environment.
     */
    static public function tearDownAfterClass()
    {
        \stream_wrapper_restore( 'http' );
    }

    /**
     * Example test case.
     */
    public function test_setSaveFile()
    {
        // You can also specify the filename for the local cache.
        Http::setSaveFilename( 'test-example' );

        // Will generate a file:  ./fixtures/test-example.rsd
        \file_get_contents( 'http://www.example.com' );

        $file = FIXTURES_PATH . DIRECTORY_SEPARATOR . $fileName . '.rsd';
        $this->assertTrue( \file_exists($file) );
        \unlink( $file );
    }
}

如何以简化方式使用 PHPUnit 的拦截测试监听器

拦截功能包含一个 PHPUnit 测试监听器,允许您使用注解简化单元测试中的请求保存。这可以作为手动方式的替代品,并在运行时自动保存和提供 HTTP 请求。但是,需要一些额外的设置。

  1. 设置一个常量,指向您希望请求保存的路径。如果您有单元测试引导文件,可以这样做
<?php
...
// Define a FIXTURES_PATH constant in your bootstrap file, set to whatever path you like.
$fixturesPath = \realpath( __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' );
\define( 'FIXTURES_PATH', $fixturesPath );
...
?>
  1. 在您的 PHP Unit 配置文件中,添加监听器如下
<?xml version="1.0" encoding="utf-8" ?>
<phpunit bootstrap="tests/bootstrap.php"
         strict="true"
         checkForUnintentionallyCoveredCode="true">
     ...
    <listeners>
        <listener class="\Kshabazz\Interception\InterceptionListener">
            <arguments>
                <!-- The first parameter must be the Interception class that will handle to protocol. -->
                <string>\Kshabazz\Interception\StreamWrappers\Http</string>
                <!-- The second parameter can be a path or a constant that is set to a path. -->
                <string>FIXTURES_PATH</string>
                <!-- The third parameter should list the protocols to handle. -->
                <array>
                    <element>
                        <string>http</string>
                    </element>
                    <element>
                        <string>https</string>
                    </element>
                </array>
            </arguments>
        </listener>
    </listeners>
</phpunit>
  1. 现在您可以编写单元测试并使用 "@interception" 注解。
/**
 * Now the HTTP request will be stored in the file "example-dot-com.rsd"
 *
 * @interception example-dot-com
 */
public function test_interception_annotation()
{
    $responseBody = file_get_content( 'http://www.example.com/', 'r' );
    $filename = FIXTURES_PATH . DIRECTORY_SEPARATOR . 'example-dot-com.rsd';
    $this->assertFileExists( \file_exists($filename) );
}

如何拦截多个 HTTP(S) 请求

当使用 InterceptionListener 时,可以通过使用 "@interceptionPersist " 注解在一个单元测试中拦截多个 HTTP 请求。如下所示

/**
* Now the HTTP request will be stored in the file "example-dot-com.rsd"
*
* @interceptionPersist example-dot-com
*/
public function test_interception_annotation()
{
    $responseBody = file_get_content( 'http://www.example.com/', 'r' );
    $responseBody = file_get_content( 'http://www.example.com/', 'r' );

    $filename1 = FIXTURES_PATH . DIRECTORY_SEPARATOR . 'example-dot-com-1.rsd';
    $filename2 = FIXTURES_PATH . DIRECTORY_SEPARATOR . 'example-dot-com-2.rsd';

    $this->assertFileExists( \file_exists($filename1) );
    $this->assertFileExists( \file_exists($filename2) );
}

请注意,每次您发起请求时,文件名都会附加请求的编号。

如何与 Guzzle 一起使用此功能

use \GuzzleHttp\Client,
    \Kshabazz\Interception\StreamWrappers\Http,
    \Kshabazz\Interception\GuzzleHandler;

// Set the file to save the response to, in your unit test, you could set this with the "@interception" annotation also.
Http::setSaveFilename( 'google-dot-come' );

// Interception Guzzle compatible stream handler
$streamHandler = new GuzzleHandler();

// Have Guzzle use the Interception stream handler, so request can be intercepted.
$httpClient = new Client([
	'handler' => $streamHandler
]);

// Make the request.
$httpClient->get( 'http://www.google.com/ );

如何更新响应。

  1. 删除 fixtures 目录中的 *.rsd 文件,以便下次运行测试时保存一个新的。

运行单元测试

./vendor/bin/phpunit