LoginSignup
16
14

More than 5 years have passed since last update.

go-tcptestを使って、echoサーバをテストする

Posted at

go-tcptest
https://github.com/lestrrat/go-tcptest

go-tcptestは、PerlのTest::TCPをGo言語に移植したモジュール。

Perl版と同じく、以下のような面倒を見てくれている。

  • 空いているポートを探す
  • TCPサーバが立ち上がり、ポートがListen状態になるまで待つ

これを使って自作のechoサーバをテストする場合について考えた。
以下のようなechoサーバを書いたとする。

echoserver.go
package echoserver

import (
        "bufio"
        "fmt"
        "net"
        "strconv"
)

type Server struct {
        Host string
        Port int
}

func (s *Server) Run() error {
        server, err := net.Listen("tcp", s.Host+":"+strconv.Itoa(s.Port))
        if err != nil {
                return err
        }
        conns := s.ClientConns(server)
        for {
                go s.HandleConn(<-conns)
        }
        return nil
}

func (s *Server) ClientConns(listener net.Listener) chan net.Conn {
        ch := make(chan net.Conn)
        i := 0
        go func() {
                for {
                        client, err := listener.Accept()
                        if err != nil {
                                fmt.Printf("couldn't accept: " + err.Error())
                                continue
                        }
                        i++
                        fmt.Printf("%d: %v <-> %v\n", i, client.LocalAddr(), client.RemoteAddr())
                        ch <- client
                }
        }()
        return ch
}

func (s *Server) HandleConn(client net.Conn) {
        b := bufio.NewReader(client)
        for {
                line, err := b.ReadBytes('\n')
                if err != nil { // EOF, or worse
                        break
                }
                client.Write(line)
        }
}

テストコードは以下のようにした。

echoserver_test.go
package echoserver

import (
        "bufio"
        "fmt"
        "github.com/lestrrat/go-tcptest"
        "log"
        "net"
        "strconv"
        "testing"
        "time"
)

func TestRun(t *testing.T) {
        echo := func(port int) {
                server := &Server{Host: "localhost", Port: port}
                err := server.Run()
                if err != nil {
                        t.Error("Error")
                }
        }

        server, err := tcptest.Start(echo, 30*time.Second)
        if err != nil {
                t.Error("Failed to start echoserver: %s", err)
        }

        log.Printf("echoserver started on port %d", server.Port())

        conn, err := net.Dial("tcp", "localhost:"+strconv.Itoa(server.Port()))
        if err != nil {
                t.Error("Failed to connect to echoserver")
        }
        fmt.Fprintf(conn, "test hogehoge\n")
        res, err := bufio.NewReader(conn).ReadString('\n')
        if res != "test hogehoge\n" {
                t.Error("Wrong Response")
        }
}

Go版では、
forkではなくgoroutineでサーバを立ち上げているので、
少し勝手が違った。

goroutineにシグナル送ったり、
外から終了させたりすることができないので、
終了処理は行っていない。

サーバ起動コマンドを作ればexec.Commandで実行して、
exec.Process.Signalでシグナルを送ることができるのだけれど、
他に方法はないものだろうか。。。

参考

A simple echo server testing a few interesting Go language features, goroutines and channels
https://gist.github.com/paulsmith/775764

16
14
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
14