如何使用PHPUnit对套接字代码进行单元测试?

8

我目前有一个Socket类,基本上只是PHP的socket_*函数的面向对象包装类:

class Socket {
    public function __construct(...) {
        $this->_resource = socket_create(...);
    }

    public function send($data) {
        socket_send($this->_resource, $data, ...);
    }

    ...
}

我认为我无法模拟套接字资源,因为我正在使用PHP的套接字函数,所以现在我陷入了如何可靠地对这个类进行单元测试的困境中。


据我所见,省略的部分“...”很重要。 - KingCrunch
@KingCrunch:它们很重要,因为这是我想要测试的内容,但对于我的问题来说并不重要。由于socket_*函数依赖于套接字资源(据我所知无法模拟),我无法可靠地测试这些方法。 - FtDRbwLXw6
我对网络编程单元测试有一些看法,但是我没有 PHP 的经验。如果几天内没有 PHP 开发人员回复,我会写一些评论。如果您发布一些使用您的套接字类的代码,那将有助于我的回答。 - selbie
根据我的回答所得到的反应,我认为这个问题可能存在严重缺陷。如果我的回答甚至没有接近正确答案,你应该考虑大幅修改它。 - Theodore R. Smith
请参阅http://stackoverflow.com/questions/11280506/how-to-mock-built-in-php-socket-functions/11280543#11280543,那里有类似的问题,并提供了代码示例,展示如何模拟PHP内置函数。 - Darren Cook
1个回答

16

你似乎缺少单元测试思维的一小部分。

通过创建一个 Stub 对象,你的问题可以很容易地解决。奇怪的是,我一遍又一遍地给出这个答案,所以它肯定被很多人忽略了。

由于我看到有太多关于 stubs 和 mocks 之间差异的混淆,让我在这里阐述一下...

  • Mock 是一个类,继承了测试直接依赖的另一个类,以改变那个类的行为,使测试更容易进行
  • Stub 是一个类,*实现了测试无法轻松直接测试的 API 或接口**,以使测试成为可能

^--那是我读过的两者最清晰的描述;我应该把它放在我的网站上。

套接字有一个很好的特性,就是你可以绑定到端口0进行测试(真的,它被称为“临时端口”)。

所以试试这个:

class ListeningServerStub
{
    protected $client;

    public function listen()
    {
        $sock = socket_create(AF_INET, SOCK_STREAM, 0);

        // Bind the socket to an address/port
        socket_bind($sock, 'localhost', 0) or throw new RuntimeException('Could not bind to address');

        // Start listening for connections
        socket_listen($sock);

        // Accept incoming requests and handle them as child processes.
        $this->client = socket_accept($sock);
    }

    public function read()
    {
        // Read the input from the client – 1024 bytes
        $input = socket_read($this->client, 1024);
        return $input;
    }
}

在测试的setUp()中创建此对象并设置为监听状态,在tearDown()中停止监听并销毁它。然后,在您的测试中,连接到虚假服务器,通过read()函数获取数据,并进行测试。

如果这对您有所帮助,请考虑给我一个赏金,以表示我超越了传统思维方式;-)


我想要更清楚地表达:如果我对客户端进行存根处理,但防火墙阻止了所选端口上的网络流量会发生什么。或者某些PHP设置禁止套接字打开。或者我们在没有root权限的系统上选择一个<1024的端口。有很多事情可能会导致测试失败,而代码并不是问题所在,这会导致测试不可靠。 - FtDRbwLXw6
1
如果你想要帮助单元测试一个不属于你的Socket类的服务器,那么你对单元测试的理解是非常混淆的。如果你想要测试一个服务器,那么只需要切换到客户端模拟(显然!)。如果以上两种情况都不是, 那不仅问题很糟糕,而且我也感到困惑,唉。 - Theodore R. Smith
1
嗨,伙计。这都是在本地测试。你的单元测试只需要依赖于你的本地网络堆栈没有任何严重的错误。 - Theodore R. Smith
2
非常好,西奥多,我正需要这样的解决方案。 - Antoine 'hashar' Musso
@TheodoreR.Smith,我能问一下你提供的解决方案是如何工作的吗?因为如果你调用socket_accept,单元测试将会一直卡在那里,直到有新的连接。 - Kamil
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接