Yusuke Hosonuma
May 18, 2019
11k

標準パッケージにおけるテストでの利用例から学ぶ testing / quick パッケージ/golang-testing-quick

Go Conference 2019 Spring の発表資料
https://gocon.jp/

May 18, 2019

Transcript

1 Go Conference 2019 Spring 2019/5/18ʢSatʣ   ࡉপ ༞հ@DeNA

3. ॴଐ • 48&5άϧʔϓˏ%F/" • ςετࣗಈԽνʔϜɿJ04(Pݴޠ ޷͖ͳ΋ͷ • ݴޠɿ4XJGUɺ)BTLFMMɺ-JTQʢʁʣ • झຯɿɺαΠΫϦϯάɺొࢁ

ʮϒϥοΫϘοΫεςετʯͷͨΊͷ6UJMJUZΛఏڙ

ʮϒϥοΫϘοΫεςετʯͷͨΊͷ6UJMJUZΛఏڙ ୺తʹݴͬͯ͠·͑͹ɺ ʮϥϯμϜ஋ʯΛ࢖ͬͨςετ

1 ʹ 1 1 ʴ 1 ʹ 2 ௨ৗͷςετ

1 ʹ 1 1 ʴ 1 ʹ 2 ௨ৗͷςετ ೖྗ஋

1 ʹ 1 1 ʴ 1 ʹ 2 ௨ৗͷςετ ೖྗ஋ ظ଴஋

0 ʹ 1 1 ʴ 1 ʹ 2 ௨ৗͷςετ ೖྗ஋ ظ଴஋ ໌֬ͳೖྗ஋ͱظ଴஋Λ ༩͑ͯݕূ͢Δ

"testing" func TestAdd(t *testing.T) { tests := []struct { x int y int want int }{ {x: 0, y: 1, want: 1}, // 0 + 1 = 1 {x: 1, y: 1, want: 2}, // 1 + 1 = 2 } for _, tt := range tests { if got := Add(tt.x, tt.y); got != tt.want { t.Errorf("Add() = %v, want %v", got, tt.want) } } }

"testing" func TestAdd(t *testing.T) { tests := []struct { x int y int want int }{ {x: 0, y: 1, want: 1}, // 0 + 1 = 1 {x: 1, y: 1, want: 2}, // 1 + 1 = 2 } for _, tt := range tests { if got := Add(tt.x, tt.y); got != tt.want { t.Errorf("Add() = %v, want %v", got, tt.want) } } } ໌֬ͳʮೖྗ஋ʯͱʮظ଴஋ʯΛ༩͑Δ

y ʹ z y ʴ x ʹ z’ ϥϯμϜςετ

y ʹ z y ʴ x ʹ z’ ϥϯμϜςετ ೖྗ஋ YͱZΛϥϯμϜʹੜ੒

y ʹ z y ʴ x ʹ z’ ϥϯμϜςετ ೖྗ஋ ଍͠ࢉ͸ަ׵๏ଇΛຬͨ͢ͷͰ [ͱ[`͸ৗʹҰக͢Δ

y ʹ z y ʴ x ʹ z’ ϥϯμϜςετ ೖྗ஋ ଍͠ࢉ͸ަ׵๏ଇΛຬͨ͢ͷͰ [ͱ[`͸ৗʹҰக͢Δ ϥϯμϜ஋ʹରͯ͠ ຬͨ͢΂͖ੑ࣭Λݕূ͢Δ

( "testing" "testing/quick" ) func TestAddQuick(t *testing.T) { f := func(x, y int) bool { return Add(x, y) == Add(y, x) } if err := quick.Check(f, nil); err != nil { t.Error(err.Error()) } }

( "testing" "testing/quick" ) func TestAddQuick(t *testing.T) { f := func(x, y int) bool { return Add(x, y) == Add(y, x) } if err := quick.Check(f, nil); err != nil { t.Error(err.Error()) } } UFTUJOHRVJDLΛJNQPSU

( “testing" "testing/quick" ) func TestAddQuick(t *testing.T) { f := func(x, y int) bool { return Add(x, y) == Add(y, x) } if err := quick.Check(f, nil); err != nil { t.Error(err.Error()) } } νΣοΫ༻ͷϔϧύʔؔ਺ ϥϯμϜͳೖྗ஋͕ೖͬͯ͘Δ૝ఆ

( "testing" "testing/quick" ) func TestAddQuick(t *testing.T) { f := func(x, y int) bool { return Add(x, y) == Add(y, x) } if err := quick.Check(f, nil); err != nil { t.Error(err.Error()) } } ަ׵๏ଇΛຬͨͤ͹USVF

( "testing" "testing/quick" ) func TestAddQuick(t *testing.T) { f := func(x, y int) bool { return Add(x, y) == Add(y, x) } if err := quick.Check(f, nil); err != nil { t.Error(err.Error()) } } ؔ਺͕ৗʹUSVFΛฦ͔͢ςετ

( "testing" "testing/quick" ) func TestAddQuick(t *testing.T) { f := func(x, y int) bool { return Add(x, y) == Add(y, x) } if err := quick.Check(f, nil); err != nil { t.Error(err.Error()) } } USVFΛฦ͞ͳ͍৔߹͸&SSPS͕ฦ٫

Add(x, y int) int { return x - y } // ίϯιʔϧग़ྗ --- FAIL: TestAddQuick (0.00s) #1: failed on input -8046014121776288896, -2929560796466791850

Add(x, y int) int { return x - y } // ίϯιʔϧग़ྗ --- FAIL: TestAddQuick (0.00s) #1: failed on input -8046014121776288896, -2929560796466791850 ަ׵๏ଇΛຬͨ͞ͳ͍ʮҾ͖ࢉʯ

Add(x, y int) int { return x - y } // ίϯιʔϧग़ྗ --- FAIL: TestAddQuick (0.00s) #1: failed on input -8046014121776288896, -2929560796466791850 Y Z

Add(x, y int) int { return x - y } // ίϯιʔϧग़ྗ --- FAIL: TestAddQuick (0.00s) #1: failed on input -8046014121776288896, -2929560796466791850 Y Z ͜ΕΒͷ஋ͷ৔߹ʹࣦഊͨ͠ͱ͍͏ใࠂ

&YBNQMFCBTFE5FTU ⁃ ໌֬ͳೖྗ஋ͱظ଴஋Λ༩͑Δςετ

&YBNQMFCBTFE5FTU ⁃ ໌֬ͳೖྗ஋ͱظ଴஋Λ༩͑Δςετ • 1SPQFSUZCBTFE5FTU ⁃ ϥϯμϜ஋ʹج͍ͮͨςετ ⁃ ຬͨ͢΂͖ੑ࣭ʢQSPQFSUZʣΛݕূ ⁃ )BTLFMMͷ2VJDL\$IFDL͕ൃ঵

&YBNQMFCBTFE5FTU ⁃ ໌֬ͳೖྗ஋ͱظ଴஋Λ༩͑Δςετ • 1SPQFSUZCBTFE5FTU ⁃ ϥϯμϜ஋ʹج͍ͮͨςετ ⁃ ຬͨ͢΂͖ੑ࣭ʢQSPQFSUZʣΛݕূ ⁃ )BTLFMMͷ2VJDL\$IFDL͕ൃ঵ ʢͨͿΜʣRVJDLͱ͍͏໋໊͸͔͜͜Β

• ໌നͳ࣮૷ • ͍͍ͨͯͷ৔߹ɺ஗͍ • ྫɿϑΟϘφον਺ྻΛ࠶ؼͰղ͘ • ͦ͏Ͱͳ͍࣮૷ • ͍͍ͨͯͷ৔߹ɺෳࡶ • ྫɿϑΟϘφον਺ྻΛ຤ඌ࠶ؼͰղ͘

• ໌നͳ࣮૷ • ͍͍ͨͯͷ৔߹ɺ஗͍ • ྫɿϑΟϘφον਺ྻΛ࠶ؼͰղ͘ • ͦ͏Ͱͳ͍࣮૷ • ͍͍ͨͯͷ৔߹ɺෳࡶ • ྫɿϑΟϘφον਺ྻΛ຤ඌ࠶ؼͰղ͘ ͲͪΒ΋ಉ͡ೖྗʹରͯ͠͸ ඞͣಉ݁͡ՌΛฦ͢͸ͣ

ConstantTimeByteEq returns 1 if x == y and 0 otherwise. func ConstantTimeByteEq(x, y uint8) int { return int((uint32(x^y) - 1) >> 31) } ςετର৅

ConstantTimeByteEq returns 1 if x == y and 0 otherwise. func ConstantTimeByteEq(x, y uint8) int { return int((uint32(x^y) - 1) >> 31) } YͱZΛఆ਺࣌ؒͰൺֱ͢Δ Ұக͢Ε͹ɺҰக͠ͳ͚Ε͹Λฦ͢ ςετର৅

byteEq(a, b uint8) int { if a == b { return 1 } return 0 } func TestConstantTimeByteEq(t *testing.T) { err := quick.CheckEqual(ConstantTimeByteEq, byteEq, nil) if err != nil { t.Error(err) } } ςετίʔυ

byteEq(a, b uint8) int { if a == b { return 1 } return 0 } func TestConstantTimeByteEq(t *testing.T) { err := quick.CheckEqual(ConstantTimeByteEq, byteEq, nil) if err != nil { t.Error(err) } } ఆ਺࣌ؒͰ͸ͳ͍໌നͳൺֱؔ਺ ςετίʔυ

byteEq(a, b uint8) int { if a == b { return 1 } return 0 } func TestConstantTimeByteEq(t *testing.T) { err := quick.CheckEqual(ConstantTimeByteEq, byteEq, nil) if err != nil { t.Error(err) } } ςετ༻ͷؔ਺ ςετίʔυ

byteEq(a, b uint8) int { if a == b { return 1 } return 0 } func TestConstantTimeByteEq(t *testing.T) { err := quick.CheckEqual(ConstantTimeByteEq, byteEq, nil) if err != nil { t.Error(err) } } ͭͷؔ਺ʹ͍ͭͯಉ݁͡ՌΛฦ͢͜ͱ ςετର৅ ໌നͳ࣮૷ ςετίʔυ

byteEq(a, b uint8) int { if a == b { return 1 } return 0 } func TestConstantTimeByteEq(t *testing.T) { err := quick.CheckEqual(ConstantTimeByteEq, byteEq, nil) if err != nil { t.Error(err) } } ͭͷؔ਺ʹ͍ͭͯಉ݁͡ՌΛฦ͢͜ͱ ςετର৅ ໌നͳ࣮૷ ಉ͡࢓༷ʢͷ͸ͣʣͷؔ਺Ͱ͋Ε͹ CheckEqualؔ਺ͰಉҰੑΛݕূͰ͖Δ ৽چͷΞϧΰϦζϜൺֱʹ΋࢖͑Δ

ςετ • 'V[[ςετ • ༧ଌෆՄೳͳೖྗσʔλʢGV[[ʣΛ༩͑ɺ  ෆ۩߹͕ൃੜ͢Δέʔε͕ແ͍͔୳͢

ςετ • 'V[[ςετ • ༧ଌෆՄೳͳೖྗσʔλʢGV[[ʣΛ༩͑ɺ  ෆ۩߹͕ൃੜ͢Δέʔε͕ແ͍͔୳͢ ϥϯμϜͳ Fuzz ςετ͸ ࣮֬Ͱ͸ͳ͍͕҆৺ײʹͭͳ͕Δ

A Block represents a PEM encoded structure. // // The encoded form is: // -----BEGIN Type----- // Headers // base64-encoded Bytes // -----END Type—— // where Headers is a possibly empty sequence of Key: Value lines. type Block struct { Type string Headers map[string]string Bytes []byte } // Encode writes the PEM encoding of b to out. func Encode(out io.Writer, b *Block) error { } ςετର৅

A Block represents a PEM encoded structure. // // The encoded form is: // -----BEGIN Type----- // Headers // base64-encoded Bytes // -----END Type----- // where Headers is a possibly empty sequence of Key: Value lines. type Block struct { Type string Headers map[string]string Bytes []byte } // Encode writes the PEM encoding of b to out. func Encode(out io.Writer, b *Block) error { } 1&.ܗࣜͷσʔλߏ଄ ςετର৅

A Block represents a PEM encoded structure. // // The encoded form is: // -----BEGIN Type----- // Headers // base64-encoded Bytes // -----END Type----- // where Headers is a possibly empty sequence of Key: Value lines. type Block struct { Type string Headers map[string]string Bytes []byte } // Encode writes the PEM encoding of b to out. func Encode(out io.Writer, b *Block) error { } Τϯίʔυ༻ͷؔ਺ ςετର৅

TestFuzz(t *testing.T) { testRoundtrip := func(block Block) bool { var buf bytes.Buffer if err := Encode(&buf, &block); err != nil { t.Errorf("Encode of %#v resulted in error: %s", &block, err) return false } decoded, rest := Decode(buf.Bytes()) if !reflect.DeepEqual(decoded, &block) { t.Errorf("Encode of %#v decoded as %#v", &block, decoded) return false } return true } quick.Check(testRoundtrip, nil) } ςετίʔυ

TestFuzz(t *testing.T) { testRoundtrip := func(block Block) bool { var buf bytes.Buffer if err := Encode(&buf, &block); err != nil { t.Errorf("Encode of %#v resulted in error: %s", &block, err) return false } decoded, rest := Decode(buf.Bytes()) if !reflect.DeepEqual(decoded, &block) { t.Errorf("Encode of %#v decoded as %#v", &block, decoded) return false } return true } quick.Check(testRoundtrip, nil) } ςετ༻ͷؔ਺ ςετίʔυ

TestFuzz(t *testing.T) { testRoundtrip := func(block Block) bool { var buf bytes.Buffer if err := Encode(&buf, &block); err != nil { t.Errorf("Encode of %#v resulted in error: %s", &block, err) return false } decoded, rest := Decode(buf.Bytes()) if !reflect.DeepEqual(decoded, &block) { t.Errorf("Encode of %#v decoded as %#v", &block, decoded) return false } return true } quick.Check(testRoundtrip, nil) } ೚ҙͷσʔλͰΤϯίʔυʹࣦഊ͠ͳ͍͜ͱ ςετίʔυ

TestFuzz(t *testing.T) { testRoundtrip := func(block Block) bool { var buf bytes.Buffer if err := Encode(&buf, &block); err != nil { t.Errorf("Encode of %#v resulted in error: %s", &block, err) return false } decoded, rest := Decode(buf.Bytes()) if !reflect.DeepEqual(decoded, &block) { t.Errorf("Encode of %#v decoded as %#v", &block, decoded) return false } return true } quick.Check(testRoundtrip, nil) } σίʔυͨ͠ΒݩͱҰக͢Δ͜ͱ Τϯίʔυɾσίʔυͨ݁͠Ռ ݩσʔλ ςετίʔυ