单元测试套接字。

10

大家好,

我有一个应用程序,其中存在一个通信抽象层。这个层中的每个实现都被称为连接器(connector),为我的应用程序提供了与已连接对等体交换数据的方法(例如:通过HTTP、TCP sockets、UDP sockets等)。

例如,我有一个名为Connector_Tcp的类,它实现了诸如readwriteopenclose之类的方法。

我需要为该类编写单元测试。我知道单元测试应尽可能少依赖于其他资源。不幸的是,在这种情况下,依赖关系是一个系统资源:一个socket;而我无法绕过它。

我需要一些关于如何进行单元测试这个类的建议。

我认为,即使这个类正在使用一个系统资源,也应该像所有其他的connectors一样进行测试,以确保它符合我的应用程序所建立的标准。

我担心的是绑定冲突(Address already in use错误)和阻塞等问题。我不希望由于端口已经被与我的应用程序无关的系统服务占用而导致单元测试失败。

我曾经做过很多单元测试,但从未依赖于如此低级别的资源,即sockets。

你会如何进行一个依赖socket的类的单元测试呢?为每个单元打开一个socket?使用一个单独的服务器类,然后手动定义socket资源连接到它并测试它...?

我想我的问题实际上是:

如果单元测试失败... 我怎么知道:

  • socket已经被另一个进程占用了;
  • 单元测试编写得不好;或者
  • 方法行为不当(这就是单元测试的价值所在)。

我需要单元测试只测试该方法是否已经正确执行,而不关心其他原因。


1
我尚未进行过真正的PHP开发,但您能否创建一个从您的Socket类派生的模拟类? - Dave
@Dave:我可以。对于每个需要连接器的类,我都使用模拟对象。我的问题在于:我需要测试套接字类本身,以确保它遵守抽象层的约定……通过测试模拟对象,它并不能清楚地告诉我套接字类是否有效。 - netcoder
在我看来,单元测试的实际限制是有的。我不会尝试对套接字进行单元测试。我猜在你的情况下,由于套接字可能在应用程序外部可访问,你只需要尽力编写围绕连接器的单元测试,并确保它处理所有异常情况。你提到了“地址已在使用”错误,所以我会做的一件事是确保我的连接器不仅仅因为这种情况而死掉。我会编写一个单元测试来创建这个条件,并确保它能够捕获它。 - Dave
2个回答

5

我写过类似的抽象层,尽管是用C/C++实现的,并且我曾经有机会对基于socket的代码进行单元测试。我不知道这里是否有任何与PHP特定相关的东西,所以我将提供我所能提供的通用建议。

  • 在设置中创建服务器套接字,在拆除时关闭它。根据您是否支持许多客户端连接到服务器套接字,您可能需要在测试方法中完成该部分。
  • 假设您可以在客户端代码中指定要连接的端口,请让服务器套接字绑定到端口0(临时端口),然后在客户端代码中使用此端口。这将消除绑定到已使用端口的问题。
  • 在套接字上设置超时时间。如果您的测试由于某些意外原因失败,您不希望它永远等待连接。
  • 您需要能够交错客户端/服务器行为。例如,您将想要测试发生了什么,如果服务器在客户端读取/写入一半时关闭其连接的一侧。异步I/O或多线程都可以胜任此工作。我不确定您在PHP中拥有什么可用资源。

我认为我需要一位C程序员的建议,因为PHP套接字的处理方式。在PHP中,套接字函数与C套接字非常相似(它们甚至使用相同的常量名称!)。话虽如此,我认为你已经指出了我的正确方向。通过绑定端口0并使用getsockname,我可以检索服务器所绑定的端口号并连接客户端而不必担心端口已被占用。我应该早点想到这个!谢谢你! - netcoder
哦,至于多线程和多个并发连接,我不需要担心。有另一个类来处理这个问题,所以我在这里只需要测试套接字本身。 - netcoder

2
我希望我能正确理解您的问题...如果不是,请告诉我。但是我会模拟套接字。这将让您编写一系列单元测试,以确保您的方法在各种情况下都执行了正确的操作。
例如,您可以编写一个单元测试,告诉套接字模拟实例抛出“端口不可用”异常。在这种情况下,您可以断言您的方法捕获了该异常,并使用与异常中相同的文本记录了它。
更重要的是,您可以编写一个单元测试,告诉套接字模拟实例从read()返回一个有效的字节流,然后您可以断言您的方法是否使用了这些字节。
因此,您实际上并没有测试套接字。您正在设置套接字以模拟某些事件,并测试以查看处理套接字的代码是否执行了正确的操作。
(我使用过Java、C#、Ruby和JavaScript中的模拟框架。我从未使用过PHP,但我敢打赌,有一些很好的PHP模拟框架)。
编辑:是的,有一些PHP模拟框架。我不能推荐其中任何一个,但这里有几个我能找到的:PHPUnitLastCraft

谢谢你的回答。我已经在使用PHPUnit了。不幸的是,与我也熟悉的Java和C#不同,PHP中的套接字非常接近于C/C++套接字。实际上,PHP使用C库的包装器。PHP套接字不是对象,而是被视为资源(或多或少是指针),更像是一种原始类型,因此无法进行模拟。我的类是这些套接字函数的包装器,我需要确保它们在必要时返回falsenull或数据,而不会因冲突而失败。我认为@axw可能有我需要的答案。非常感谢! - netcoder

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