TDD和模拟TcpClient

时间:2020-03-06 14:53:34  来源:igfitidea点击:

人们如何模仿TcpClient(或者类似TcpClient之类的东西)?

我有接受TcpClient的服务。我应该将其包装在其他更可笑的东西中吗?我应该如何处理?

解决方案

当进入不适合测试的模拟类时(即密封/未实现任何接口/方法不是虚拟的),我们可能希望使用适配器设计模式。

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

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();
   }
}

使用适配器模式绝对是解决问题的标准TDD方法。但是,我们也可以只创建TCP连接的另一端,并让测试工具驱动该连接。

IMO适配器类的广泛使用使设计中最重要的部分变得模糊不清,并且还倾向于从测试中删除很多本应在上下文中进行测试的内容。因此,替代方法是建立测试支架,以包括更多正在测试的系统。如果我们是从头开始构建测试,那么我们仍将能够将失败原因隔离到给定的类或者函数中,而不会孤立...

我认为@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相关的细节完全脱钩(如果这是设计目标),甚至可以从硬编码的一组值或者测试输入文件中引入测试输入。如果我们正准备长期测试代码,请非常有帮助。