donatj/mock-webserver

简单的模拟Web服务器,用于单元测试

v2.7.2 2024-01-22 20:41 UTC

README

Latest Stable Version License ci.yml

简单的、易于使用的模拟Web服务器,用于PHP单元测试。与PHPUnit和其他单元测试框架简单兼容。

单元测试HTTP请求可能很困难,尤其是在难以注入请求库或不是最佳选择的情况下。这有助于大大简化过程。

模拟Web服务器创建一个本地Web服务器,你可以针对它进行预定义的请求。

文档

见:docs/docs.md

要求

  • php: >=7.1
  • ext-sockets: *
  • ext-json: *
  • ralouphie/getallheaders: ~2.0 || ~3.0

安装

使用以下命令安装最新版本

composer require --dev 'donatj/mock-webserver'

示例

基本用法

以下示例显示了最基本的用法。如果你没有定义路径,服务器将简单地回弹一个描述请求的JSON体。

<?php

use donatj\MockWebServer\MockWebServer;

require __DIR__ . '/../vendor/autoload.php';

$server = new MockWebServer;
$server->start();

$url = $server->getServerRoot() . '/endpoint?get=foobar';

echo "Requesting: $url\n\n";
echo file_get_contents($url);

输出

Requesting: http://127.0.0.1:61874/endpoint?get=foobar

{
    "_GET": {
        "get": "foobar"
    },
    "_POST": [],
    "_FILES": [],
    "_COOKIE": [],
    "HEADERS": {
        "Host": "127.0.0.1:61874",
        "Connection": "close"
    },
    "METHOD": "GET",
    "INPUT": "",
    "PARSED_INPUT": [],
    "REQUEST_URI": "\/endpoint?get=foobar",
    "PARSED_REQUEST_URI": {
        "path": "\/endpoint",
        "query": "get=foobar"
    }
}

简单

<?php

use donatj\MockWebServer\MockWebServer;
use donatj\MockWebServer\Response;

require __DIR__ . '/../vendor/autoload.php';

$server = new MockWebServer;
$server->start();

// We define the server's response to requests of the /definedPath endpoint
$url = $server->setResponseOfPath(
	'/definedPath',
	new Response(
		'This is our http body response',
		[ 'Cache-Control' => 'no-cache' ],
		200
	)
);

echo "Requesting: $url\n\n";

$content = file_get_contents($url);

// $http_response_header is a little known variable magically defined
// in the current scope by file_get_contents with the response headers
echo implode("\n", $http_response_header) . "\n\n";
echo $content . "\n";

输出

Requesting: http://127.0.0.1:61874/definedPath

HTTP/1.1 200 OK
Host: 127.0.0.1:61874
Date: Tue, 31 Aug 2021 19:50:15 GMT
Connection: close
X-Powered-By: PHP/7.3.25
Cache-Control: no-cache
Content-type: text/html; charset=UTF-8

This is our http body response

更改默认响应

<?php

use donatj\MockWebServer\MockWebServer;
use donatj\MockWebServer\Responses\NotFoundResponse;

require __DIR__ . '/../vendor/autoload.php';

$server = new MockWebServer;
$server->start();

// The default response is donatj\MockWebServer\Responses\DefaultResponse
// which returns an HTTP 200 and a descriptive JSON payload.
//
// Change the default response to donatj\MockWebServer\Responses\NotFoundResponse
// to get a standard 404.
//
// Any other response may be specified as default as well.
$server->setDefaultResponse(new NotFoundResponse);

$content = file_get_contents($server->getServerRoot() . '/PageDoesNotExist', false, stream_context_create([
	'http' => [ 'ignore_errors' => true ], // allow reading 404s
]));

// $http_response_header is a little known variable magically defined
// in the current scope by file_get_contents with the response headers
echo implode("\n", $http_response_header) . "\n\n";
echo $content . "\n";

输出

HTTP/1.1 404 Not Found
Host: 127.0.0.1:61874
Date: Tue, 31 Aug 2021 19:50:15 GMT
Connection: close
X-Powered-By: PHP/7.3.25
Content-type: text/html; charset=UTF-8

VND.DonatStudios.MockWebServer: Resource '/PageDoesNotExist' not found!

PHPUnit

<?php

use donatj\MockWebServer\MockWebServer;
use donatj\MockWebServer\Response;

class ExampleTest extends PHPUnit\Framework\TestCase {

	/** @var MockWebServer */
	protected static $server;

	public static function setUpBeforeClass() : void {
		self::$server = new MockWebServer;
		self::$server->start();
	}

	public function testGetParams() : void {
		$result  = file_get_contents(self::$server->getServerRoot() . '/autoEndpoint?foo=bar');
		$decoded = json_decode($result, true);
		$this->assertSame('bar', $decoded['_GET']['foo']);
	}

	public function testGetSetPath() : void {
		// $url = http://127.0.0.1:61874/definedEndPoint
		$url    = self::$server->setResponseOfPath('/definedEndPoint', new Response('foo bar content'));
		$result = file_get_contents($url);
		$this->assertSame('foo bar content', $result);
	}

	public static function tearDownAfterClass() : void {
		// stopping the web server during tear down allows us to reuse the port for later tests
		self::$server->stop();
	}

}

延迟响应的使用

默认情况下,响应将立即发生。如果你想要测试超时,DelayedResponse响应包装器可能很有用。

<?php

use donatj\MockWebServer\DelayedResponse;
use donatj\MockWebServer\MockWebServer;
use donatj\MockWebServer\Response;

require __DIR__ . '/../vendor/autoload.php';

$server = new MockWebServer;
$server->start();

$response = new Response(
	'This is our http body response',
	[ 'Cache-Control' => 'no-cache' ],
	200
);

// Wrap the response in a DelayedResponse object, which will delay the response
$delayedResponse = new DelayedResponse(
	$response,
	100000 // sets a delay of 100000 microseconds (.1 seconds) before returning the response
);

$realtimeUrl = $server->setResponseOfPath('/realtime', $response);
$delayedUrl  = $server->setResponseOfPath('/delayed', $delayedResponse);

echo "Requesting: $realtimeUrl\n\n";

// This request will run as quickly as possible
$start = microtime(true);
file_get_contents($realtimeUrl);
echo "Realtime Request took: " . (microtime(true) - $start) . " seconds\n\n";

echo "Requesting: $delayedUrl\n\n";

// The request will take the delayed time + the time it takes to make and transfer the request
$start = microtime(true);
file_get_contents($delayedUrl);
echo "Delayed Request took: " . (microtime(true) - $start) . " seconds\n\n";

输出

Requesting: http://127.0.0.1:61874/realtime

Realtime Request took: 0.015669107437134 seconds

Requesting: http://127.0.0.1:61874/delayed

Delayed Request took: 0.10729098320007 seconds

同一端点的多个响应

响应堆栈

如果你需要一个有序的响应集,可以使用ResponseStack。

<?php

use donatj\MockWebServer\MockWebServer;
use donatj\MockWebServer\Response;
use donatj\MockWebServer\ResponseStack;

require __DIR__ . '/../vendor/autoload.php';

$server = new MockWebServer;
$server->start();

// We define the servers response to requests of the /definedPath endpoint
$url = $server->setResponseOfPath(
	'/definedPath',
	new ResponseStack(
		new Response("Response One"),
		new Response("Response Two")
	)
);

echo "Requesting: $url\n\n";

$contentOne = file_get_contents($url);
$contentTwo = file_get_contents($url);
// This third request is expected to 404 which will error if errors are not ignored
$contentThree = file_get_contents($url, false, stream_context_create([ 'http' => [ 'ignore_errors' => true ] ]));

// $http_response_header is a little known variable magically defined
// in the current scope by file_get_contents with the response headers
echo $contentOne . "\n";
echo $contentTwo . "\n";
echo $contentThree . "\n";

输出

Requesting: http://127.0.0.1:61874/definedPath

Response One
Response Two
Past the end of the ResponseStack

按方法响应

如果你需要通过方法改变单个端点的响应,可以使用ResponseByMethod响应对象。

<?php

use donatj\MockWebServer\MockWebServer;
use donatj\MockWebServer\Response;
use donatj\MockWebServer\ResponseByMethod;

require __DIR__ . '/../vendor/autoload.php';

$server = new MockWebServer;
$server->start();

// Create a response for both a POST and GET request to the same URL

$response = new ResponseByMethod([
	ResponseByMethod::METHOD_GET  => new Response("This is our http GET response"),
	ResponseByMethod::METHOD_POST => new Response("This is our http POST response", [], 201),
]);

$url = $server->setResponseOfPath('/foo/bar', $response);

foreach( [ ResponseByMethod::METHOD_GET, ResponseByMethod::METHOD_POST ] as $method ) {
	echo "$method request to $url:\n";

	$context = stream_context_create([ 'http' => [ 'method' => $method ] ]);
	$content = file_get_contents($url, false, $context);

	echo $content . "\n\n";
}

输出

GET request to http://127.0.0.1:61874/foo/bar:
This is our http GET response

POST request to http://127.0.0.1:61874/foo/bar:
This is our http POST response