2016-06-30 6 views
-2

Wenn ich Code habe, der mit einer net.Conn funktioniert, wie kann ich Tests dafür schreiben, ohne tatsächlich eine Netzwerkverbindung zu localhost zu erstellen?Wie können Sie Code testen, der auf net.Conn basiert, ohne eine tatsächliche Netzwerkverbindung zu erstellen?

Ich habe online keine Lösungen zu diesem Thema gesehen; Leute scheinen es entweder zu ignorieren (keine Tests), schreiben Tests, die nicht parallel laufen können (dh verwenden Sie eine tatsächliche Netzwerkverbindung, die Up-Ports verwendet), oder verwenden Sie io.Pipe.

Jedoch definiert net.ConnSetReadDeadline, SetWriteDeadline; und io.Pipe nicht. net.Pipe auch tut, trotz vordergründig um die Schnittstelle zu implementieren behauptet, es ist einfach mit umgesetzt:

func (p *pipe) SetDeadline(t time.Time) error { 
    return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} 
} 

func (p *pipe) SetReadDeadline(t time.Time) error { 
    return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} 
} 

func (p *pipe) SetWriteDeadline(t time.Time) error { 
    return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} 
} 

(siehe: https://golang.org/src/net/pipe.go)

So ... gibt es eine andere Art und Weise, dies zu tun?

Ich werde jede Antwort akzeptieren, die zeigt, wie man einen Stream in einem Test mit einem Arbeitsende verwendet, das kein tatsächlicher Netzwerk-Socket ist.

(Idly, diese cloudflare blogpost deckt die Motivation für die Verwendung von Deadlines, und warum Blockieren für immer in einer Goroutine pro Verbindung ist keine akzeptable Lösung; aber unabhängig von diesem Argument, insbesondere in diesem Fall suche ich nach einer Lösung für testet, wo wir, wo eine schlechte Verbindung hängt, etc.)

(NB. dies wie ein Duplikat Simulate a tcp connection in Go scheinen mag, aber feststellen, dass die alle vorgeschlagenen Lösungen in dieser Frage bewusst behandeln Fälle Kante wollen nicht die Frist umsetzen Funktionen, die genau das ist, was ich frage, wie man hier testet)

+3

Warum können Sie keine Netzwerkverbindung verwenden? Die meisten Tests, die ein net.Conn erfordern, verwenden normalerweise einen anderen lokalen Endpunkt unter Kontrolle des Tests. – JimB

+0

Sie müssen das Protokoll der Netzwerkkarte überprüfen (Ethernet/WLAN, ...). – bilelovitch

+0

Sicherlich einen tatsächlichen Netzwerkanschluss pro Test zu skalieren, skaliert nicht, wenn Sie viele viele Tests haben? (parallel laufen ...) Was machst du, wählst einen zufälligen Port und hoffst? Weisen Sie einen Port-Pool zu und binden Sie die Ports optimistisch, bis Sie einen freien finden? – Doug

Antwort

5

Ihre Frage ist sehr offen, daher ist es nicht möglich, Ihnen "die richtige Antwort" zu geben. Aber ich glaube, ich verstehe den Punkt, an dem du feststeckst. Diese Antwort ist auch offen, aber sie sollte dich wieder auf den richtigen Weg bringen.

Vor ein paar Tagen schrieb ich eine short article, die das Prinzip zeigt, das Sie verwenden müssen.

Bevor ich einige kleine Beispiele zu machen, wie Sie solche Tests arbeiten, müssen wir einen wichtigen Punkt beheben:

Wir testen nicht das Netto-Paket. Wir nehmen an, dass das Paket keinen Fehler hat und tut, was die Dokumentation sagt. Das bedeutet wir kümmern uns nicht, wie das Go-TeamSetReadDeadline und SetWriteDeadline implementiert hat. Wir testen nur die Verwendung in unserem Programm.

Schritt 1: Refactoring Code

Sie haben keinen Code-Schnipsel veröffentlichen, so dass ich Ihnen nur ein einfaches Beispiel. Ich nehme an, Sie haben eine Methode oder Funktion, wo Sie das Netzpaket verwenden.

func myConn(...) error { 
    // You code is here 
    c, err := net.Dial("tcp", "12.34.56.78:80") 
    c.setDeadline(t) 
    // More code here 
} 

Dass Sie in der Lage sind Sie zu testen müssen Ihre Funktion Refactoring, so ist es nur die net.Conn Schnittstelle. Dazu muss der net.Dial() Aufruf außerhalb der Funktion verschoben werden. Denken Sie daran, dass wir die net.Dial-Funktion nicht testen möchten.

:

func myConn(c, net.Conn, ...) error { 
    // You code is here 
    c.setDeadline(t) 
    // More code here 
} 

Schritt 2:

Für die Prüfung müssen Sie implementieren, um die net.Conn Schnittstelle implementieren die net.Conn Schnittstelle

Die neue Funktion so etwas aussehen könnte

type connTester struct { 
    deadline time.Time 
} 

func (c *connTester) Read(b []byte) (n int, err error) { 
    return 0, nil 
} 

... 

func (c *connTester) SetDeadline(t time.Time) error { 
    c.deadline = t 
    return nil 
} 

... 

Vollständige Implementierung einschließlich einer kleinen Typprüfung: https://play.golang.org/p/taAmI61vVz

Schritt 3: Testen

Beim Testen uns über die Dial() Methode ist es egal, wir einen Zeiger auf unsere Testtype nur erstellen, die den net.Conn-Schnittstelle implementiert und in Ihre Funktion setzen . Danach schauen wir uns unsere Testfälle an, wenn der Deadline-Parameter korrekt eingestellt ist.

func TestMyConn(t *testing.T){ 
    myconnTester = &connTester{} 
    err := myConn(myconnTester,...) 
    ... 
    if myconntester.deadline != expectedDeadline{ 
    //Test fails 
    } 
} 

Also beim Testen sollten Sie immer darüber nachdenken, welche Funktion Sie testen möchten. Ich denke, es ist der wirklich schwierigste Teil, die Funktionalität, die Sie wirklich schreiben möchten, zu abstrahieren. Innerhalb einfacher Unit-Tests sollten Sie niemals Funktionalitäten der Standard-Library testen. Ich hoffe, diese Beispiele können Ihnen helfen, Sie wieder auf den richtigen Weg zu bringen.

2

Code, der ausgetauscht werden muss, sollte für eine kontrollierte Version in einem Unittest hinter einer Abstraktion stehen. In diesem Fall wäre die Abstraktion die net.Conn Schnittstelle. Der Produktionscode würde go std lib net.Conn verwenden, aber der Testcode würde einen Test-Stub verwenden, der mit der genauen Logik konfiguriert ist, um Ihre Funktion auszuüben.

Die Einführung einer Abstraktion ist ein leistungsfähiges Muster, das den Austausch aller IO- oder Timing-basierten Codes ermöglichen sollte, um die kontrollierte Ausführung von Code während eines Unittests zu ermöglichen.

@apxp erklärte den gleichen Ansatz in einem Kommentar.

Derselbe Ansatz sollte für die Deadlines funktionieren. Es könnte schwierig werden, eine Frist zu simulieren, die erreicht wird, da Sie möglicherweise Ihren Stub mit mehreren Antworten konfigurieren müssen. dh. Die erste Antwort ist erfolgreich, aber die zweite simuliert eine Frist, die erreicht wurde, und löst einen Fehler für die zweite Anforderung aus.

+0

Können Sie in Bezug auf Fristen genauer sein? Ich kann nicht sehen, wie man diesen Rat praktisch anwendet. Der einzige praktische Weg, den ich sehen kann, scheint etwas wie ein Gabel-Netz zu sein. Aber wie mache ich das eigentlich? Ich weiß nicht ... – Doug

Verwandte Themen