kshabazz / interception
一个PHP库,充当流包装器函数的中介,例如fopen和fsockopen;用于记录网络请求并回放。
Requires (Dev)
- guzzlehttp/guzzle: ~5.0
- phpunit/phpunit: ~4.1
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,不受支持。
- 请求 - 包含头和有效负载,保存为*.rsd
- 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 请求。但是,需要一些额外的设置。
- 设置一个常量,指向您希望请求保存的路径。如果您有单元测试引导文件,可以这样做
<?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 ); ... ?>
- 在您的 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>
- 现在您可以编写单元测试并使用 "@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/ );
如何更新响应。
- 删除 fixtures 目录中的 *.rsd 文件,以便下次运行测试时保存一个新的。
运行单元测试
./vendor/bin/phpunit