34

I'm currently looking into creating some unit tests for net.Conn interface in Go, as well as other functions that build up on top of that functionality, and I'm wondering what is the best way to unit test that in Google Go? My code looks like:

conn, _:=net.Dial("tcp", "127.0.0.1:8080")
...
fmt.Fprintf(conn, "test")
...
buffer:=make([]byte, 100)
conn.Read(buffer)

Is the most efficient way of testing this code and the code that uses these functions to spin up a separate goroutine to act like the server, use net.http.httptest package, or something else?

ThePiachu
  • 8,695
  • 17
  • 65
  • 94
  • 2
    Suggest reading the source for the tests for the actual `net` library. I have picked up plenty of tips by doing that in the past. Secondly as you have already mentioned use the `httptest` package. – miltonb Jun 07 '15 at 04:31
  • There are several libraries for *mocking* in unit testing, take a look at this post for hints http://stackoverflow.com/questions/19167970/mock-functions-in-golang – PerroVerd Jun 16 '15 at 08:33

3 Answers3

127

You might be able to do what you need with net.Pipe which basically gives you both ends of a connection (think, after .Accept())

server, client := net.Pipe()
go func() {
  // Do some stuff
  server.Close()
}()

// Do some stuff
client.Close()
Freman
  • 1,271
  • 2
  • 8
  • 2
  • 14
    This is the greatest answer for testing tcp connections without starting a server. Should be the accepted or at least more up-voted answer imho. Thank you! – luben Apr 01 '17 at 13:02
  • Yes, this is genius! You don't need the overhead of actually using TCP. You could in theory parallelize your tests running by using this design, since it does not reserve global ports from the oprating system. – joonas.fi Dec 05 '17 at 09:54
  • 3
    You may also look at "google.golang.org/grpc/test/bufconn" - it implements in-memory net.Listener in net.Dialer where connections are "pipes", but unlike net.Pipe they are also buffered which makes them easier to use in some situations. – Tomasz Elendt Sep 21 '18 at 09:57
  • 4
    Can you give an example of how you would test a WebSocket connection (gorilla) in this example with this approach, please. – Mohamed Bana Mar 04 '19 at 21:44
  • 1
    an example related to `net.pipe` https://stackoverflow.com/questions/36932741/how-can-i-properly-write-the-read-and-write-the-net-pipe – JuanPablo Nov 05 '20 at 19:41
14

Although it will depend on the implementation details of your particular case, the general approach will be to start a server (in a separate goroutine, as you already hinted), and listen to the incoming connections.

For example, let's spin up a server and verify that the content we are reading from the connection is indeed the one we send over from the client:

func TestConn(t *testing.T) {
    message := "Hi there!\n"

    go func() {
        conn, err := net.Dial("tcp", ":3000")
        if err != nil {
            t.Fatal(err)
        }
        defer conn.Close()

        if _, err := fmt.Fprintf(conn, message); err != nil {
            t.Fatal(err)
        }
    }()

    l, err := net.Listen("tcp", ":3000")
    if err != nil {
        t.Fatal(err)
    }
    defer l.Close()
    for {
        conn, err := l.Accept()
        if err != nil {
            return
        }
        defer conn.Close()

        buf, err := ioutil.ReadAll(conn)
        if err != nil {
            t.Fatal(err)
        }

        fmt.Println(string(buf[:]))
        if msg := string(buf[:]); msg != message {
            t.Fatalf("Unexpected message:\nGot:\t\t%s\nExpected:\t%s\n", msg, message)
        }
        return // Done
    }

}

Note that here I'm not starting the server in the goroutine, as otherwise the test case is likely to be finished before the listener has run the test.

Iñigo
  • 12,723
  • 2
  • 31
  • 31
2

Another option is the counterfeiter package which lets you create mocks from interfaces and then you can stub out whatever calls you need. I have used it with great success to stub out net.Conn instances where I am testing out a protobuf client for Geode.

For example - https://github.com/gemfire/geode-go-client/blob/master/connector/protobuf_test.go

Jens D
  • 4,229
  • 3
  • 16
  • 19