その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Goのhttptestパッケージを使ってUNIX domain socketを使ったHTTPサーバのテストをする

Goが提供するHTTPのテストのためのユーティリティであるところのhttptestを使って、UNIX domain socketを使用するHTTPサーバのテストをするという内容についてのメモです。

import (
	"log"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
)

func TestFoo(t *testing.T) {
	mux := http.NewServeMux()
	mux.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
		_, err := w.Write([]byte("hello"))
		if err != nil {
			log.Printf("error: %s", err)
			w.WriteHeader(500)
			return
		}
		w.WriteHeader(200)
	})

	tempSock, err := os.CreateTemp("", "sock")
	if err != nil {
		t.Fatal(err)
	}
	err = os.Remove(tempSock.Name())
	if err != nil {
		t.Fatal(err)
	}

	listener, err := net.Listen("unix", tempSock.Name())
	if err != nil {
		t.Fatal(err)
	}

	server := httptest.NewUnstartedServer(mux)
	server.Listener = listener
	server.Start()
	defer server.Close()

	// do something
}
  • os.CreateTemp()でtemporaryなファイルを作り、それを削除してファイル名だけを引っ張りつつ (削除せず実ファイルが残っているとbind(2)がEADDRINUSEを吐く)
  • それを net.Listen()に渡してlistenerを作り
  • httptest.NewUnstartedServer()でテスト用のサーバのインスタンスを作り
  • listenerをそのサーバのインスタンスに食わせ
  • サーバスタート
  • あとはUNIX domain socketを使ったクライアントを使ってテストを書く

という感じで使うことが可能です。簡単ですね。


もちろんtemp fileを使わずに任意のパスをソケットファイルとして与えても良い (server.Close()が実行されるとそのソケットファイルも削除されるので) のですが、うっかりserver.Close()が実行されないとゴミファイルが残ってしまったりしてダルいので、まあ保険のようなものです。