没有看过任何代码就很难争论。根据您的描述,听起来您有一门课可以做所有事情。这可能已经成为使测试变得比实际更难的原因。
谈到单元测试,我喜欢坚持 Michael Feathers 的定义:
如果满足以下条件,则测试不是单元测试:
- 它与数据库对话。
- 它通过网络进行通信。
- 它涉及文件系统。
- 它不能与您的任何其他单元测试同时运行。
- 您必须对您的环境做一些特殊的事情(例如编辑配置文件)才能运行它。
模拟 Ratchet 和 RabbitMQ 连接似乎是使测试符合上述定义的一种方式,但还有另外一句话:“不要模拟你不拥有的东西”。虽然这不是一个硬性规定,但我认为这是一个很好的指导方针。有很多关于这个主题的文章。 This should give you a good overview
不过,Ratchet 在服务器和事件循环部分有点特别。但它经过了很好的测试,所以你不必这样做。我的建议:保持与 Ratchet 的集成如此简单,反正没有太多要测试的东西,直接跳过测试。
final class MyRatchetApp implements MessageComponentInterface
{
private MessageProcessor $processor;
public function __construct(MessageProcessor $processor)
{
$this->processor = $processor;
}
public function onMessage(ConnectionInterface $from, $msg)
{
$this->processor->handle($msg);
}
// ...
}
这会给你留下一个MessageProcessor,你可以单独测试它。它的所有依赖项都是您拥有的类型,因此您可以使用模拟、存根或假实现来测试它们的交互。此实现过于简化,并且遗漏了您肯定想做并且当然想测试的错误处理等内容。
final class MessageProcessor
{
private MessageParser $parser;
private MessageBroadcaster $broadcaster;
public function __construct(MessageParser $parser, MessageBroadcaster $broadcaster)
{
$this->parser = $parser;
$this->broadcaster = $broadcaster;
}
public function handle(string $rawMessage): void
{
$this->broadcaster->send($this->parser->parse($rawMessage));
}
}
interface MessageParser
{
/**
* @throws BadMessageException
*/
public function parse(string $message): Message;
}
interface MessageBroadcaster
{
/**
* @throws UnsupportedMessageException
* @throws UnroutableMessageException
*/
public function send(Message $message): void;
}
创建MessageParser 的实现应该简单明了且易于进行单元测试。 MessageBroadcaster 的 RabbitMQ 实现将是集成测试的完美候选者。
最后,为真正的应用程序将所有具体实现组合在一起。
$server = IoServer::factory(
new MyRatchetApp(
new MessageProcessor(
new CommandMessageParser(),
new RabbitMqMessageBroadcaster()
)
),
8080
);
为确保所有部分协同工作,您可以创建一些端到端测试来执行完整的往返并验证结果。首先创建这些测试,允许您执行Double Loop TDD