Upload
takuya-ueda
View
1.126
Download
0
Embed Size (px)
Citation preview
The Go gopher was designed by Renée French.The gopher stickers was made by Takuya Ueda.Licensed under the Creative Commons 3.0 Attributions license.
Go Friday傑作選
@golang.tokyo #92017/09/29
自己紹介
上田拓也@tenntenn
2
所属
コミュニティ活動
&
Go ビギナーGo Conference
Go Friday■ GoやGCPに関するソウゾウの社内勉強会
● 社外で得た知見の共有
○ 海外カンファレンスについての共有
○ コミュニティで得た知識の共有
● 社内の各プロダクト開発で得た知見の共有
○ 設計の方針や実装方法の共有
○ 共通ライブラリの共有
● 社外へのアウトプットの練習
○ 発表練習
○ イベント登壇のネタ出し
○ 社内ライブラリのOSS化の相談
参考:https://codeiq.jp/magazine/2017/04/50250/
switchを使おう
const (StatusA = 0StatusB = 1StatusC = 0 // miss
)switch s {
case StatusA:fmt.Println("A")
case StatusB:fmt.Println("B")
case StatusC: // errorfmt.Println("C")
}
ついにIsTemporaryを実装したぞう
■ 用途● 一時的なエラーを表現する● タスクキューのエラーでリトライすればそのうち直るもの● エラーログとして書き出されるとslackに通知されて困るもの
type temporary interface { Temporary() bool}
func IsTemporary(err error) bool { te, ok := err.(temporary) return ok && te.Temporary()}
参考:http://deeeet.com/writing/2016/04/25/go-pkg-errors/
@mitchellh のテストの話よい
■ Advanced Testing with Go● https://speakerdeck.com/mitchellh/advanced-testing-with-go● テーブルドリブンテスト
● サブテスト
● ヘルパー関数
テーブル駆動テスト
■ テスト対象のデータを羅列してテストする
参考:https://github.com/golang/go/wiki/TableDrivenTests
var flagtests = []struct {in stringout string
}{{"%a", "[%a]"}, {"%-a", "[%-a]"}, {"%+a", "[%+a]"},{"%#a", "[%#a]"}, {"% a", "[% a]"},
}func TestFlagParser(t *testing.T) {
var flagprinter flagPrinterfor _, tt := range flagtests {
s := Sprintf(tt.in, &flagprinter)if s != tt.out {
t.Errorf("Sprintf(%q, &flagprinter) => %q, want %q", tt.in, s, tt.out)}
}}
サブテスト
■ 子テストを実行するしくみ● Go1.7から導入された
func TestAB(t *testing.T) {t.Run("A", func(t *testing.T) { t.Error("error") })t.Run("B", func(t *testing.T) { t.Error("error") })
}
go test -v sample -run TestAB/A=== RUN TestAB=== RUN TestAB/A--- FAIL: TestAB (0.00s) --- FAIL: TestAB/A (0.00s) sample_test.go:10: errorFAILexit status 1FAIL sample 0.007s
サブテストを指定して実行
サブテストとテーブル駆動テスト
func TestIsOdd(t *testing.T) {cases := []*struct {name string; input int; expected bool}{
{name: "+odd", input: 5, expected: true},{name: "+even", input: 6, expected: false},{name: "-odd", input: -5, expected: true},{name: "-even", input: -6, expected: false},{name: "zero", input: 0, expected: false},
}
for _, c := range cases {t.Run(c.name, func(t *testing.T) {
if actual := IsOdd(c.input); c.expected != actual {t.Errorf(
"want IsOdd(%d) = %v, got %v",c.input, c.expected, actual)}})
}}
テストヘルパー
■ テスト用のヘルパー関数● ヘルパー関数はエラーを返さない● *testing.Tを受け取ってテストを落とす● Go 1.9からはT.Helperを使って情報を補足する
○ https://tip.golang.org/pkg/testing/#T.Helper
func testTempFile(t *testing.T) string { t.Helper() tf := ioutil.TempFile("", "test") if err != nil { t.Fatal("err %s", err) } tf.Close() return tf.Name()}
APIクライアントを作る時のテストどうしよう
■ ハンドラのテストはnet/http/httptestを使う
● ハンドラのテストのための機能など提供
● ResponseRecorder○ ResponseWriterを実装している
● NewRequestメソッド(1.7以上)○ 簡単にテスト用のリクエストが作れる
ハンドラのテストの例func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello, net/http!")}
func TestSample(t *testing.T) {w := httptest.NewRecorder()r := httptest.NewRequest("GET", "/", nil)handler(w, r)rw := w.Result()defer rw.Body.Close()if rw.StatusCode != http.StatusOK { t.Fatal("unexpected status code") }b, err := ioutil.ReadAll(rw.Body)if err != nil { t.Fatal("unexpected error") }const expected = "Hello, net/http!"if s := string(b); s != expected { t.Fatalf("unexpected response: %s", s) }
}
テスト対象
テストコード
HTTPハンドラのモック
■ net/http/httptest.Serverを使う● https://play.golang.org/p/KADrbDUEBp
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {fmt.Fprintln(w, r.RequestURI)
}))defer ts.Close()
tsURL, err := url.Parse(ts.URL)if err != nil { log.Fatal(err) }
client := ts.Client()client.Transport = &http.Transport{ Proxy: http.ProxyURL(tsURL) }
res, err := client.Get("http://example.com")if err != nil { log.Fatal(err) }
HTTPSだと動かない!
HTTPSの場合
■ RoundTriper を実装し、URLを書き換える● https://play.golang.org/p/WSSal0bVMs
type RewriteTransport struct {Transport http.RoundTripperURL *url.URL
}
func (t RewriteTransport) RoundTrip(req *http.Request) (*http.Response, error) {req.URL.Scheme = t.URL.Schemereq.URL.Host = t.URL.Hostreq.URL.Path = path.Join(t.URL.Path, req.URL.Path)rt := t.Transportif rt == nil { rt = http.DefaultTransport }return rt.RoundTrip(req)
}リクエストを書き換えるのはあまりよくないが ...
HTTPSの場合
■ RoundTriper を実装し、URLを書き換える● https://play.golang.org/p/WSSal0bVMs
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "hello %s\n", path.Base(r.URL.Path))
}))u, err := url.Parse(s.URL)
if err != nil { log.Fatalln("Error:", err) }cli := s.Client()cli.Transport = RewriteTransport{URL: u, Transport: cli.Transport}resp, err := cli.Get("https://example.com")
if err != nil { log.Fatalln("Error:", err) }io.Copy(os.Stdout, resp.Body)resp.Body.Close()
Thank you!
twitter: @tenntenn Qiita: tenntennconnpass: tenntenn
17