リトライ処理のテストをしたかったのでやり方を調べた。

res, err := client.Do(req)
if err != nil {
    // このときリトライするようにしているのをテストしたい
}

タイムアウトでもerrを返すため、clientのタイムアウトを極端に短くしてhttptest.Serverでちょっと待つようにすれば再現できるが、あまりスマートじゃない気がした。

コネクションを切断する

http.Hijacker interfaceを使うことでコネクションを乗っ取ることができる。

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
 
	"github.com/hashicorp/go-retryablehttp"
	"github.com/stretchr/testify/assert"
)
 
func TestRetry(t *testing.T) {
	t.Run("リトライしたときに成功する", func(t *testing.T) {
		var count int
		ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			switch count {
			case 0:
			    // 乗っ取り
				c, _, err := w.(http.Hijacker).Hijack()
				if err != nil {
					t.Fatal(err)
				}
			    // コネクションを断つ
				c.Close()
				count++
				return
			case 1:
				fmt.Fprintf(w, "successful response")
				return
			}
		}))
		defer ts.Close()
 
		retryClient := retryablehttp.NewClient()
		retryClient.Logger = nil
		retryClient.RetryMax = 2
		retryClient.RetryWaitMax = 1 * time.Second
		got, err := retryClient.Get(ts.URL)
 
		b, _ := io.ReadAll(got.Body)
		defer got.Body.Close()
 
		assert.NoError(t, err)
		assert.Equal(t, "successful response", string(b))
	})
 
}