我在 PHP 中使用 Ratchet 进行 websockets。我能够为 websockets 编写单元(phpspec)和验收测试(behat),但我找不到如何使用功能性 phpunit 测试来测试与 websocket 服务器的连接的方法。我认为是一个测试,它检查是否连接已启动并正在运行,这非常重要。我想到了类似以下的事情:
问题是,我不知道哪个类负责在 Ratchet 中建立连接(创建可以请求 websocket 的客户端)以及测试会是什么样子。如何创建 Ratchet 客户端以便能够在 phpunit 功能测试中连接并请求 websocket? (类似于标准 phpunit 功能测试中的 Web 客户端)
举个例子,在功能测试中,我可以执行以下操作:
$client = static::createClient();
$client->request('GET', '/book/3', array(), array(), array('HTTP_X-Requested-With' => 'XMLHttpRequest'));
$response = $client->getResponse();
$this->assertEquals(
440,
$response->getStatusCode()
);
或者例如创建经过身份验证的客户端而不是匿名客户端。如何将这个功能测试“翻译”为棘轮 websocket 测试?
我希望这能部分解决您的问题:Ratchet Websocket 的哪一部分负责建立连接。
测试本身可能不起作用,我对 PHP 测试没有那么丰富的经验,尽管它应该让你走上正确的道路。
public function testCanConnect()
{
\Ratchet\Client\connect('ws://127.0.0.1:8080')->then(function($conn) {
$conn->on('message', function($msg) use ($conn) {
print $msg."\n";
$this->assertEquals('true',true);
$this->assertEquals("Hello World",$msg);
$conn->close();
});
$conn->send('Hello World!');
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});
}
如果您还有任何疑问,请告诉我。
这是一个有点难破解的难题!特别是,许多 websocket 客户端库在建立连接后永远不会返回,导致您的单元测试永远处于执行状态。到目前为止,我发现的最喜欢的解决方案是作为父 php 进程的子进程运行
wscat
,并直接在 php 中进行所有断言:
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class WebsocketsIntegrationTest extends TestCase
{
public function test_websocket_connection()
{
$descriptorspec = [
// stdin
0 => [
"pipe",
"r"
],
// stdout
1 => [
"pipe",
"w"
],
// stderr
2 => [
"pipe",
"w"
]
];
$process = proc_open(
"wscat -c wss://echo.websocket.org/?auth=123xyz",
$descriptorspec,
$pipes
);
if (is_resource($process)) {
// Allow some time for the connection to be established (max timeout of 3 seconds)
sleep(3);
/**
* Next, to test that your websocket integration works as expected, send the messages you want to, and also
* provide the response you expected to be delivered in response to it, to verify that the websockets
* server responds as expected.
*/
$messages = [
/**
* Send an empty message, to verify that the websocket service responds as intended to it.
*/
[
'request_body' => '',
'expected_response_body' => "> {\"status\" : \"connected\"}",
]
];
foreach ($messages as $message) {
fwrite(
$pipes[0],
/**
* The newline character is crucial as it corresponds to hitting enter in the CLI
* (submit the message)
*/
"{$message['request_body']}\n"
);
/**
* The websocket server is expected to respond immediately, hence waiting one second should be enough
*/
sleep(1);
}
// Close the stdin pipe, as no more input will be provided
fclose($pipes[0]);
/**
* Terminate the process to simulate CTRL + C.
* Note that this command is immediately executed, which is why the sleep calls above are needed.
*/
proc_terminate($process);
// Get the output of the stdout and stderr pipes
$output = stream_get_contents($pipes[1]);
$errors = stream_get_contents($pipes[2]);
// Close the stdout and stderr pipes
fclose($pipes[1]);
fclose($pipes[2]);
// Close the process and get the termination status
$return_value = proc_close($process);
/**
* Order will be conserved with explode, so assert the message responses in the order of their request, as
* any pipe is a FIFO structure.
*
* Every response delivered by the websocket server that was currently setup responds with an ending newline
* character in the CLI. To thus get all responses, explode the obtained stdout string via the newline
* characters as the separator, and then kick off the last element to disregard the trailing newline
* characters (which would otherwise lead to an additional empty string as expected response at index n + 1)
* You may have to adapt this according to your response logic of your websockets server
*/
$responses = explode(
separator: "\n",
string : $output
);
array_pop($responses);
foreach ($responses as $index => $response) {
/**
* Assert that the websocket server responded as expected for every submitted message
*/
$this->assertSame(
$messages[$index]['expected_response_body'],
$response,
"An unexpected response body was received from the websocket server; $response instead of the expected {$messages[$index]['expected_response_body']}."
);
}
/**
* No errors should have been encountered
*/
$this->assertEmpty(
$errors,
"Errors encountered: $errors"
);
/**
* proc_terminate() by defaults sends 15 as the signal to the running process; so that's the expected
* integer output here
*/
$this->assertEquals(
15,
$return_value,
"Process exited with code $return_value"
);
} else {
$this->fail("The command used to start the websocket server failed to execute");
}
}
}
请注意,您需要 wscat 才能正常工作,无论如何,这对于在本地测试您的 websockets 解决方案很有好处!