【问题标题】:Faking TCP requests in C#在 C# 中伪造 TCP 请求
【发布时间】:2012-04-17 11:10:47
【问题描述】:

我的 n00b 服务器的一部分:

TcpClient client = tcpListener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(handleClientRegistration));
clientThread.Start(client);
...
//and in the clientThread we wait for data
bytesRead = clientStream.Read(message, 0, 4096);

现在我想为这个 sn-p 编写单元测试。我想伪造一个客户端连接,传递任意数据并检查服务器如何处理它。

我应该如何在 C# 中做到这一点?

编辑:
我想模拟的是连接本身——我想避免网络连接。

【问题讨论】:

  • 我认为你必须真正连接到套接字上......?也许我错过了你的问题,但只需打开另一个套接字,调用 connect,然后发送一些数据......?
  • 这正是这个问题的意义所在:我应该打开套接字,还是应该模拟整个 TCP 接口。我认为正确的测试不应该使用像网络接口这样的有限资源。或者我错了。

标签: c# .net unit-testing networking mocking


【解决方案1】:

如果它正在侦听套接字,则它不是单元测试。 用一个薄层包裹客户端并使用模拟来代替。这样你就可以伪造所有你想要的接收到的数据。

例如模拟套接字(简化示例):

使用您需要的所有方法创建一个接口 ISocket:

public interface ISocket
{
    ISocket Accept( int port );
    byte[] Receive( int numberOfBytes );
    bool Send( byte[] data );
    void Disconnect();
    bool Connect( string address, int port );
}

现在创建一个实现 ISocket 的具体类 TcpSocket:

public class TcpSocket : ISocket
{
    private Socket socket;
    public TcpSocket()
    {
        socket = new Socket( AddressFamily.InterNetwork,
                             SocketType.Stream, ProtocolType.Tcp );
    }

    // implement all methods of ISocket by delegating to the internal socket
}

无论您通常在何处使用 System.Net.Sockets.Socket,都应改为传递 ISocket。当您需要新建一个 Socket 时,请使用 TcpSocket。如果您在系统内部深处创建套接字,您可能希望创建一个工厂而不是直接新建一个 TcpSocket。在测试中,您可以通过不同的 ISocket 实现(模拟)(可能通过工厂创建)。您可以通过创建称为 MockSocket 的 ISocket 的第二个实现来实现您自己的模拟,它在 Receive 中返回测试数据,或者您可以使用无数模拟框架中的一个来为您执行此操作。

public class MockSocket : ISocket
{
     private byte[] testData;
     public void SetTestData(byte[] data)
     {
         testData = data;
     }

     public byte[] Receive(int numberOfBytes)
     {
         return testData;
     }

     // you need to implement all members of ISocket ofcourse...
}

这可能看起来需要付出很多努力,但在除玩具系统之外的所有系统中,边界 api 都应该隐藏在一个薄层后面,不仅是为了测试,也是为了保持灵活性。例如,如果您想使用更强大的套接字库而不是 System.Net.Socket,您可以让 TcpSocket 使用它,而无需在您自己的代码中更改对套接字的每次使用。这也是为什么对接口进行编程而不是对具体实现进行编程通常是一个好主意的原因:您可以轻松地切换实现(但这并不意味着您应该为所有类创建接口)。如果这一切都让您感到困惑,您可以阅读更多关于模拟的内容(例如:http://en.wikipedia.org/wiki/Mock_object

【讨论】:

  • 好的。我有一个 TCP 服务器 - 线程,正在侦听 tcp 连接。我有一个客户端 - 与服务器通信。现在我正在测试服务器,那么我到底应该模拟什么......客户端?为什么..因为我可以强制它以任何方式发送任何数据。我在考虑更多关于模拟 TCP 接口的 C# 方式,因此不会产生流量等。更多的内存样式..
  • 你应该模拟 TcpClient 类。
  • 让它到底做什么?因为我有这个类的源代码,它会将任何数据发送给它的方法。
  • 让它返回测试数据?
  • 我可以在没有嘲笑的情况下做到这一点。我可以在 c# 中模拟/重定向到本地内存流的网络连接吗?
【解决方案2】:

最简单的方法是使用一个方法,该方法采用Stream 并执行您期望的任何操作。这样您就可以简单地将包含您想要的数据的MemoryStream 传递给它。这无助于测试您的客户端初始化代码,但不清楚这对您是否重要。

另外,不要为每个客户创建一个新的Thread,而是使用Task 或类似的方法。

【讨论】:

    【解决方案3】:

    为了对此进行单元测试,您可能需要在 .NET TCP API 之上创建一个抽象层。单元测试通常用于测试代码的输入、输出和交互,而不是测试代码与其他 API 的交互。这就是集成测试的作用。您将创建的抽象层不会进行单元测试,但它可以让您轻松地将其替换为假对象或模拟对象,以便测试其余代码。

    【讨论】:

      猜你喜欢
      • 2010-10-13
      • 2021-10-17
      • 2014-06-21
      • 1970-01-01
      • 1970-01-01
      • 2012-08-14
      • 2013-02-28
      • 1970-01-01
      • 2013-07-17
      相关资源
      最近更新 更多