在C#中伪造TCP请求

时间:2012-04-03 18:38:56

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

我的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);

现在我想为这个片段编写单元测试。我想假冒客户端连接,传递任意数据并检查服务器如何处理它。

我应该如何在C#中完成?

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

3 个答案:

答案 0 :(得分:16)

如果正在侦听套接字,则不是单元测试。 包裹客户端和薄层,然后使用模拟。通过这种方式,您可以伪造所需的所有接收数据。

E.g。模拟套接字(简化示例):

使用您需要的所有方法创建一个接口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

答案 1 :(得分:3)

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

此外,不要为每个客户启动新的Thread,而是使用Task或类似的方法。

答案 2 :(得分:2)

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