PR TIMES CTOでPodcast「八百万のOSS」ホストの金子達哉(catatsuy)氏が、Go Conference 2026での発表を基に執筆した記事です。2017年9月から約9年保守しているCLIツール「notify_slack」は、標準入力の出力をターミナルへは即座に、Slackへはデフォルト1秒ごとにまとめて投稿するツールで、入力を読む reader goroutine とバッファをflushする処理が並行動作するためテストが難しいという課題を抱えていました。
2017年時点のテストはruntime.GOMAXPROCS(1)とtime.Sleepを使った時間ベースの脆弱な同期に依存していましたが、Go 1.25で導入されたtesting/synctestにより「時間を待つ」から「状態を待つ」方式への転換が起きました。synctest.Wait()は他のgoroutineが「durably blocked」状態になるまで待機する仕組みで、これを使ったテストによりio.PipeReader.ReadLine()のblocking readがcontextのキャンセルでは中断されないという問題も発見されたそうです。
さらにT.SetenvやT.Context、Go 1.27で導入されたhttptest.NewTestServer(t, handler)(productionと同じURLでリクエストを作成しつつ通信だけをテストサーバーへ転送する仕組み)など、標準ライブラリの進化が手作りのworkaroundを不要にしていった過程が具体的に紹介されています。
出典が伝えている要点
- 著者は金子達哉(catatsuy)で、株式会社PR TIMESのCTOである
- 著者はPodcast「八百万のOSS」のホストを務めている
- この記事はGo Conference 2026(gocon26)での発表内容に基づく
- 著者は「notify_slack」というCLIツールを2017年9月から約9年間保守している
- notify_slackは長時間実行コマンドの出力をSlackへ投稿するCLIであり、標準入力から受け取った出力をターミナルへは即座に、Slackへはデフォルト1秒ごとにまとめて投稿する
- notify_slackはEOFまたはCtrl-C時に残りの出力を送り切る
- notify_slackの実装では、標準入力を読む「reader goroutine」とバッファをflushする処理が並行して動作する
- 2017年時点のテストはruntime.GOMAXPROCS(1)とtime.Sleep(time.Millisecond)を使った時間ベースの同期に依存していた
- Go 1.25で導入されたtesting/synctestパッケージにより、「時間を待つ」方式から「状態を待つ」方式へ同期方法が転換された
- synctest.Wait()は、他のgoroutineが「durably blocked」状態になるまで待機する機能である
- synctestを使ったテストにより、io.PipeReader.ReadLine()のようなblocking readがcontextのキャンセルでは中断されないという問題が発見された
- Go 1.27でhttptest.NewTestServer(t, handler)が導入され、専用のClientを使ってproductionと同じURLでリクエストを作成しつつ通信だけをテストサーバーへ転送できるようになった