10 Stimmen

Fälschen von TCP-Anfragen in C#

Teil meines N00b-Servers:

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

Jetzt möchte ich Unit-Tests für dieses Snippet schreiben. Ich möchte eine Client-Verbindung vortäuschen, beliebige Daten übergeben und prüfen, wie der Server sie verarbeitet.

Wie sollte ich das in C# machen?

bearbeiten:
Ich möchte mich über die Verbindung selbst lustig machen - ich möchte Netzwerkverbindungen vermeiden.

18voto

EricSchaefer Punkte 23715

Es handelt sich nicht um einen Einheitstest, wenn er auf einen Socket hört. Umhüllen Sie den Client und mit einer dünnen Schicht und arbeiten Sie stattdessen mit einem Mock. Auf diese Weise können Sie alle empfangenen Daten fälschen, die Sie wollen.

Z.B. Mocking the socket (vereinfachtes Beispiel):

Erstellen Sie eine Schnittstelle ISocket mit allen Methoden, die Sie benötigen:

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

Erstellen Sie nun eine konkrete Klasse TcpSocket, die ISocket implementiert:

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
}

Wo immer Sie normalerweise einen System.Net.Sockets.Socket verwenden würden, übergeben Sie stattdessen einen ISocket. Wenn Sie einen Socket neu erstellen müssen, verwenden Sie TcpSocket. Wenn Sie Sockets tief in den Eingeweiden Ihres Systems erstellen, möchten Sie vielleicht eine Factory erstellen, anstatt einen TcpSocket direkt neu zu erstellen. Im Test können Sie dann eine andere Implementierung von ISocket (den Mock) übergeben (möglicherweise durch die Factory erstellt). Sie könnten Ihren eigenen Mock implementieren, indem Sie eine zweite Implementierung von ISocket mit dem Namen MockSocket erstellen, die Testdaten in Receive zurückgibt, oder Sie könnten eines der unzähligen Mock-Frameworks verwenden, um dies für Sie zu tun.

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...
}

Dies mag wie ein großer Aufwand erscheinen, aber in allen Systemen, außer in Spielzeugsystemen, sollte die Boundary-Api hinter einer dünnen Schicht versteckt werden, nicht nur zum Testen, sondern auch um flexibel zu bleiben. Wenn Sie z.B. eine leistungsfähigere Socket-Bibliothek anstelle von System.Net.Socket verwenden wollen, können Sie TcpSocket dazu bringen, diese zu verwenden, ohne dass Sie jede Verwendung von Sockets in Ihrem eigenen Code ändern müssen. Das ist auch der Grund, warum es normalerweise eine gute Idee ist, auf eine Schnittstelle statt auf eine konkrete Implementierung zu programmieren: Sie können die Implementierungen leicht wechseln (was aber nicht bedeutet, dass Sie Schnittstellen für ALLE Ihre Klassen erstellen sollten). Wenn Sie das alles verwirrt, lesen Sie mehr über Mocking (wie hier: http://en.wikipedia.org/wiki/Mock_object )

3voto

Guvante Punkte 18175

Der einfachste Weg wäre, eine Methode zu haben, die eine Stream und führt die von Ihnen erwarteten Operationen aus. Auf diese Weise können Sie einfach eine MemoryStream in dem die gewünschten Daten gespeichert sind. Dies wird nicht helfen, Ihren Client-Initialisierungscode zu testen, aber es war nicht klar, ob das für Sie wichtig war oder nicht.

Beginnen Sie auch keine neue Thread pro Mandant, verwenden Sie Task oder eine ähnliche Methodik zu verwenden.

3voto

Steven Oxley Punkte 6298

Um dies zu testen, müssen Sie wahrscheinlich eine Abstraktionsschicht über der .NET TCP API erstellen. Unit-Tests dienen in der Regel zum Testen der Eingaben, Ausgaben und der Interaktion Ihres Codes - nicht zum Testen der Interaktion Ihres Codes mit anderen APIs. Das ist die Aufgabe von Integrationstests. Die Abstraktionsschicht, die Sie erstellen werden, wird nicht Unit-getestet, aber Sie können sie leicht gegen ein Fake- oder Mock-Objekt austauschen, um den Rest Ihres Codes zu testen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X