Goでhttpリクエスト
- 参考 golang.org
いくつか方法はあるようだが、
client := &http.Client{ CheckRedirect: redirectPolicyFunc, } resp, err := client.Get("http://example.com") // ... req, err := http.NewRequest("GET", "http://example.com", nil) // ... req.Header.Add("If-None-Match", `W/"wyzzy"`) resp, err := client.Do(req) // ...
こっちの書き方でクライアントを実装する(http.Client
を使用する)
実装コード
httpmock
を使わない例
- 以下のように実装できる(説明のためにちょっと修正)
type HttpClient interface { Do(req *http.Request) (*http.Response, error) } func NewContentUsecase(c HttpClient) *ContentUsecase { return &ContentUsecase{ httpClient: c, } } type ContentUsecase struct { httpClient HttpClient } func (content ContentUsecase) GetContent() (string, error) { request, _ := http.NewRequest("GET", "https://google.com", nil) resp, _ := content.httpClient.Do(request) defer resp.Body.Close() byteArray, _ := ioutil.ReadAll(resp.Body) return string(byteArray), nil }
- キモとなるのは
GetContent
の引数に渡されてくるContentUsecase
- この
ContentUsecase
がHttpClient
インターフェースを満たしており、それを引数で注入する - テスト時にはこの引数に渡すものをmockすれば良い
テスト(httpmock
を使わない例)
type clientMock struct { HttpClient MockedDo func(req *http.Request) (*http.Response, error) } func (c *clientMock) Do(req *http.Request) (*http.Response, error) { return c.MockedDo(req) } func TestGetContent(t *testing.T) { t.Run("GetContent正常系", func(t *testing.T) { spyMocked := &clientMock{ MockedDo: func(req *http.Request) (*http.Response, error) { return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewBufferString("OK")), }, nil }, } contentUsecase := NewContentUsecase(spyMocked) content, err := contentUsecase.GetContent() if err != nil { t.Errorf("err should be nil: %v", err) } if content != "OK" { t.Errorf("Returned Content invalid: %v", content) } }) }
HttpClient
インターフェースの中身をMockする(Do
関数を置き換える)- 実際のテストコードの中で
Do
関数が何を返すのかを実装してやれば良い(MockedDo
を実装する)
httpmock
を使う例
- こっちのほうはとても簡単
でも、標準ライブラリのみで使える前者のほうがいいんではないかとか思ったりする
一部プログラム変更してあります(エラーハンドリングなど)
func GetContentUseMock() (string, error) { request, _ := http.NewRequest("GET", "https://example.com", nil) client := &http.Client{} resp, _ := client.Do(request) defer resp.Body.Close() byteArray, _ := ioutil.ReadAll(resp.Body) return string(byteArray), nil }
テスト
func TestGetContentUseMock(t *testing.T) { t.Run("GetContentUseMock正常系", func(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() httpmock.RegisterResponder("GET", "https://example.com", func(req *http.Request) (*http.Response, error) { res := httpmock.NewStringResponse(200, "ok") return res, nil }, ) content, err := GetContentUseMock() if err != nil { t.Errorf("error should be nil: %v", err) } if content != "ok" { t.Errorf("content invalid: %v", content) } }) }
RegisterResponder
に何を返すのかを書くだけ。