2008-09-29 60 views
8

人们如何嘲笑TcpClient(或者像TcpClient这样的东西)?TDD和嘲讽TcpClient

我有一个服务,需要一个TcpClient。我应该用其他更可笑的东西包装吗?我应该如何处理这个问题?

回答

22

当模拟不友好的测试类(即密封/不实现任何接口/方法不是虚拟的)时,您可能需要使用Adapter设计模式。

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

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

这是完美的!谢谢! – 2008-09-29 20:49:31

+0

+1最好的解释加上不错的简单例子 – Ahmad 2010-10-11 07:22:26

2

使用适配器模式是绝对的标准TDD方法来解决这个问题。但是,您也可以创建TCP连接的另一端,并使用测试设备驱动该连接。

IMO适配器类的广泛使用混淆了设计中最重要的部分,并且还倾向于从被测试中去除很多东西,这些东西应该在上下文中进行测试。因此,另一种方法是建立测试脚手架,以包括更多被测系统。如果你从头开始建立你的测试,你仍然能够将失败的原因分离到一个给定的类或函数,它不会孤立地...

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中的相关细节(如果这是一个设计目标),你甚至可以在测试输入管道从值硬编码集或测试输入文件。如果您正在测试您的代码以便长期使用,那就非常好用了。