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