Websocket 测试[功能]

问题描述 投票:0回答:2

我在 PHP 中使用 Ratchet 进行 websockets。我能够为 websockets 编写单元(phpspec)和验收测试(behat),但我找不到如何使用功能性 phpunit 测试来测试与 websocket 服务器的连接的方法。我认为是一个测试,它检查是否连接已启动并正在运行,这非常重要。我想到了类似以下的事情:

  1. 在 phpunit 中创建一个(棘轮)客户端
  2. 连接到 ws url(例如 client->connect(host, port, ...)
  3. ping websocket /发送/接收一些消息(调用客户端的方法,例如client->push(..)..)

问题是,我不知道哪个类负责在 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 测试?

php testing websocket phpunit ratchet
2个回答
4
投票

我希望这能部分解决您的问题: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";
        });
    }

如果您还有任何疑问,请告诉我。


0
投票

这是一个有点难破解的难题!特别是,许多 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 解决方案很有好处!

© www.soinside.com 2019 - 2024. All rights reserved.