TDD和模拟TcpClient

10

人们如何处理模拟 TcpClient(或类似TcpClient的东西)?

我有一个接收TcpClient的服务。我应该将其包装在更易于模拟的其他东西中吗?我应该怎样处理这个问题?

3个回答

26

当涉及到不太适合测试的模拟类(即封闭的/未实现任何接口/方法不是虚方法),您可能希望使用适配器设计模式。

在这种模式中,您添加一个实现接口的包装类。然后,您应该模拟接口,并确保所有代码都使用该接口而不是不友好的具体类。它看起来会像这样:

public interface ITcpClient
{
   Stream GetStream(); 
   // Anything you need here       
}
public class TcpClientAdapter: ITcpClient
{
   private TcpClient wrappedClient;
   public TcpClientAdapter(TcpClient client)
   {
    wrappedClient = client;
   }

   public Stream GetStream()
   {
     return wrappedClient.GetStream();
   }
}

5

我认为@Hitchhiker的想法是正确的,但我还喜欢进一步抽象化。

我不会直接模拟TcpClient,因为即使你编写了测试,这仍然会使你与底层实现过于紧密地联系在一起。也就是说,你的实现具体绑定了TcpClient的某个方法。就我个人而言,我会尝试这样做:

   [Test]
    public void TestInput(){

       NetworkInputSource mockInput = mocks.CreateMock<NetworkInputSource>();
       Consumer c = new Consumer(mockInput);

       c.ReadAll();
    //   c.Read();
    //   c.ReadLine();

    }

    public class TcpClientAdapter : NetworkInputSource
    {
       private TcpClient _client;
       public string ReadAll()
       { 
           return new StreamReader(_tcpClient.GetStream()).ReadToEnd();
       }

       public string Read() { ... }
       public string ReadLine() { ... }
    }

    public interface NetworkInputSource
    {
       public string ReadAll(); 
       public string Read();
       public string ReadLine();
    }

如果这是你的设计目标,那么这个实现将完全使你脱离Tcp相关细节,而且你甚至可以从一个硬编码的值集合或测试输入文件中输入测试数据。如果你正在进行长期代码测试,这非常有用。


2
使用适配器模式绝对是解决问题的标准TDD方法。但是,你也可以创建TCP连接的另一端,并让测试工具驱动它。
在我看来,广泛使用适配器类会模糊设计的最重要部分,并且往往会从上下文中删除许多真正应该被测试的内容。因此,另一种选择是构建测试脚手架以包括更多的待测系统。如果您从头开始构建测试,仍然可以将故障原因隔离到给定的类或函数中,只是不再是在隔离状态下...

这会使测试成为集成测试,而不是单元测试。它会变得更慢,失败的可能性更大。并不是说这有什么问题,你只需要意识到它(例如,在你的CI解决方案中)。 - Doron Yaacoby

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