Go言語で作られたAPIのレガシーコードと戦った話 / Go Conference 2015 winter

42a3fcdaaa492f3e1cf0fe7e6bbf4251?s=47 kyokomi
December 06, 2015

Go言語で作られたAPIのレガシーコードと戦った話 / Go Conference 2015 winter

Go言語で作られたAPIのレガシーコードと戦った話

42a3fcdaaa492f3e1cf0fe7e6bbf4251?s=128

kyokomi

December 06, 2015
Tweet

Transcript

  1. GoݴޠͰ࡞ΒΕͨ APIͷϨΨγʔίʔυͱઓͬͨ࿩ Go#Conference#2015#winter @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 1

  2. ࣗݾ঺հ • Twi%er:)@k_yokomi)ʢ͖ΐ͜Έʣ※1 • ࢓ࣄ͸GoͰAPIॻ͍ͯ·͢(1೥͘Β͍) • ੲ͸Cocos2d<xͱ͔৮ͬͯ·ͨ͠ʢv3.4 ͘Β͍·Ͱʣ • ࠷ۙ͸ɺGAE/Go͕ؾʹೖ͍ͬͯΔ

    ※1"ΞΠίϯ͸݄ඣͷᘡᘚ͞ΜͰ͢ɻʢ຾ΓͷԂ༷"ఏڙʣ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 2
  3. աڈʹ࡞ͬͨ΋ͷ঺հ1 • github.com/kyokomi/slackbot • slackઐ༻ͷGo੡ͷhubot • ϓϥάΠϯ௥Ճ͠΍͍͢Α͏ʹͯ͠Δͭ΋Γ • github.com/kyokomi/emoji •

    λʔϛφϧֆจࣈϥΠϒϥϦ • ૣ͍ऀউͪͰ࡞ͬͨײ͡ɻελʔ70௒͑ͨ7⭐ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 3
  4. աڈʹ࡞ͬͨ΋ͷ঺հ2 • github.com/kyokomi/go/docomo/docomo • docomoͷAPIϥΠϒϥϦʢࡶஊɺ஌ࣝQ&Aͱ͔ରԠʣ • github.com/kyokomi/goma • DBΞΫηεϑϨʔϜϫʔΫతͳ෺(JavaͷdomaΠϯεύΠΞ) •

    ҰԠɺখن໛ͳϓϩμΫγϣϯͰ࢖ͬͯΔ • ՝୊͕ଟ͘ɻݱঢ়ͷར༻͸Φεεϝ͠ͳ͍...=! @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 4
  5. ໨࣍ • ϨΨγʔίʔυ͍ͭͯ • ࣮ࡍʹग़ձͬͨϨΨγʔίʔυͷ঺հ • ϨΨγʔίʔυվળͷͨΊʹ΍ͬͨ͜ͱΛ঺հ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 5

  6. ϨΨγʔίʔυʹ͍ͭͯ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 6

  7. ϨΨγʔίʔυʹ͍ͭͯ • ۀքͰҰൠతͳղऍ • ॻ੶:#ϨΨγʔίʔυվળΨΠυͷղऍ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 7

  8. ۀքͰҰൠతͳղऍ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 8

  9. !!ཧղͰ͖ͣ!! มߋͷ೉͍͠ίʔυΛࢦ͢ଏޠɻ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 9

  10. ཧղͰ͖ͣɺมߋͷ೉͍͠ίʔυΛࢦ͢ଏޠ • ಺༰͕ෳࡶͰԿΛ͍ͯ͠Δͷ͔Α͘෼͔Βͣɺ·ͱ΋ͳ࢓༷ॻ ΋ͳ͍ • ౓ॏͳΔ࢓༷มߋͰख͕Ճ͑ΒΕͨίʔυ͸ࠞཚ͠ɺ؆୯ͳػ ೳ௥ՃͰ͋ͬͯ΋ଟ͘ͷ࿑ྗͱ࣌ؒΛඞཁͱ͢Δঢ়ଶͳͲ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 10

  11. ॻ੶ ʮϨΨγʔίʔυվળ ΨΠυʯͷղऍ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 11

  12. !!୯ʹςετ͕ͳ͍ίʔυ!! @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 12

  13. ୯ʹςετ͕ͳ͍ίʔυ ςετͷͳ͍ίʔυ͸ѱ͍ίʔυͰ͋ΔɻͲΕ͚ͩ͏·͘ॻ͔Ε ͍ͯΔ͔͸ؔ܎ͳ͍ɻͲΕ͚ͩඒ͍͔͠ɺΦϒδΣΫτࢦ޲͔ɺ ͖ͪΜͱΧϓηϧԽ͞Ε͍ͯΔ͔͸ؔ܎ͳ͍ɻςετ͕͋Ε͹ɺ ݕূ͠ͳ͕Βίʔυͷಈ͖Λૉૣ͘มߋ͢Δ͜ͱ͕Ͱ͖Δɻςε τ͕ͳ͚Ε͹ɺίʔυ͕ྑ͘ͳ͍ͬͯΔͷ͔ѱ͘ͳ͍ͬͯΔͷ͔ ͕ຊ౰ʹΘ͔Βͳ͍ɻ※2 ※2"ϨΨγʔίʔυվળΨΠυͷʮ͸͡ΊʹʯͷจষΑΓ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 13

  14. ϨΨγʔίʔυͱରቂͨ࣌͠ͷ2୒ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 14

  15. ฤूͯ͠فΔ!!!͔ อޢͯ͠มߋ͢Δ!!!͔ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 15

  16. ฤूͯ͠فΔ!!!※3 ʮฤूͯ͠فΔʯ͸ɺطଘͷίʔυΛ஫ҙਂ͘ௐ΂ͳ͕Βमਖ਼͢Δํ๏Ͱ͢ɻ ͜ͷ৔߹ͷखॱΛ૝૾ͯ͠Έ·͠ΐ͏ɻ ·ͣ͸εςοϓ͝ͱʹԿ͕ى͜Δ͔Λ໌֬ʹཧղ͠ɺίʔυมߋͷӨڹൣғΛ೺Ѳ͠·͢ɻ ࣍ʹɺ࣌ؒΛ͔͚ͯ஫ҙਂ͘ίʔυΛॻ͖ɺίʔυมߋ͕͢΂ͯऴΘͬͨΒɺفΔؾ࣋ͪͰಈ͔ͯ͠Έ·͢ɻ ͦͷ݁Ռ͸ʮ͋Εʁ มߋՕॴ͸େৎ෉͚ͩͲ……ผͷػೳ͕ಈ͔ͳ͘ͳͬͯ͠·ͬͨ……ʯɻ ʮ஫ҙΛΑΓଟ͘෷͔ͬͨΒͱ͍ͬͯɺඞͣ͠΋ͦΕʹൺྫͯ҆͠શੑ͕޲্͢Δͱ͸ݶΒͳ͍ʯͱஶऀ͸ڧ͘ड़΂͍ͯ·͢ɻ طଘίʔυʹखΛೖΕΔࡍʹ͸ɺʮ࢒ΓͷৼΔ෣͍Λม͑ͣʹࡁ·ͤΔ͜ͱ͕Ͱ͖Δ͔͕ॏཁʯͰ͕͢ɺ஫ҙਂ͍͚ͩͰ͸ɺ ଞͷ෦෼ʹӨڹ͕ٴΜͰ͍Δ͔Ͳ͏͔·Ͱ͸࠷ޙʹಈ͔ͯ͠Έͳ͍ͱ෼͔Γ·ͤΜɻ

    ͲΜͳʹ஫ҙਂ͘ਐΊͯ΋ɺ݁ہ͸ʮฤूͯ͠فΔʯ͜ͱʹͳͬͯ͠·͍·͢ɻ ※3"CodeZineʮϨΨγʔίʔυվળΨΠυʯͷεεϝΑΓ"h+p:/ /codezine.jp/ar5cle/detail/4103 @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 16
  17. อޢͯ͠ฤू͢Δ!!!※3 ΋͏1ͭͷʮอޢͯ͠มߋ͢Δʯ͸ɺςετΛ༻ҙͨ͠͏͑ͰطଘͷίʔυΛमਖ਼͢Δํ๏Ͱ͢ɻ ίʔυͷมߋ͕೉͍͠ཧ༝ΛΑ͘ߟ͑ͯΈΔͱɺςετ͕͋Ε͹ίʔυͷมߋ͕؆୯ʹͳΔͱ͍͏ɺ ҙ֎ͱ୯७ͳ͜ͱʹؾ͕͖ͭ·͢ɻ ΋͠ɺมߋ͍ͯ͠Δ࠷தʹ͏·͘มߋͰ͖͍ͯΔ͔Ͳ͏͔ɺ ଞͷ෦෼ʹӨڹ͕ٴΜͰ͍ͳ͍͔Ͳ͏͔Λ֬ೝ͢Δखஈ͕͋ͬͨͱͨ͠ΒͲ͏ͳΔͰ͠ΐ͏͔ɻ ͦͷ৔߹͸ɺগ͠มߋΛͯ͠ɺେৎ෉͔Ͳ͏͔Λ֬ೝͯ͠ɺ ·ͨগ͠มߋΛͯ͠ɺͱ͍͏࡞ۀΛ܁Γฦ͠ͳ͕ΒਐΊΔ͜ͱ͕ՄೳʹͳΓ·͢ɻ ※3"CodeZineʮϨΨγʔίʔυվળΨΠυʯͷεεϝΑΓ"h+p:/ /codezine.jp/ar5cle/detail/4103

    @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 17
  18. Ҏ্Λ౿·͑ͯ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 18

  19. ࠓճࣗ෼͕࿩͢ ϨΨγʔίʔυ͸ɾɾɾ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 19

  20. ࢓༷͕ཧղͰ͖ͣɺมߋ͕೉͘͠ ςετίʔυ͕ͳ͍GoͰ͢#! @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 20

  21. ͜ͷ1೥ APIαʔόʔΛGoͰॻ͍͍ͯͯ ࣮ࡍʹ͋ͬͨϨΨγʔίʔυΛ঺հ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 21

  22. ※஫ҙࣄ߲ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 22

  23. Goʹ͋·Γؔ܎ͳ͍಺༰΋͋Γ·͢ ͦΜͳ౰ͨΓલͷઃܭ΋ͯ͠ͳ͍ͷʁͱ͍͏ Α͏ͳ಺༰΋͋Γ·͢ ͝༰͍ࣻͩ͘͞!!!!!!!!!! @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 23

  24. ʊਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓʊ ʼɹςετίʔυ͕΄ͱΜͲͳ͍ɹʻ ʉY^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Yʉ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 24

  25. ग़Φνײ͋Γ·͕͢... • ͋Δʹ͸͋Δ͕ɺtestίʔυ͔Β࢓༷͕ಡΈऔΕͳ͍ • ϝιουΛݺͼͩͤΔͷΛ֬ೝ͢Δ͚͕ͩ໨తʹͳ͍ͬͯΔ • Test͕௨Βͳͯ͘ίϝϯτΞ΢τ͞Ε͍ͯͨΓ • ͦ΋ͦ΋ςετίʔυ͕ͳ͍ύοέʔδ΋ଟʑ͋Γ •

    globalม਺Λ͋ͪͪ͜Ͱ࢖͍ͬͯΔͨΊMock͕೉͍͠ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 25
  26. ରॲޙ ͺͬͱݟͰෆཁʹͳΓͦ͏ͳίʔυ΍ϦϑΝΫλϦϯάޙεϚʔ τʹςετॻ͚ͦ͏ɾɾɾͱ͍͏ࢥ͍Λ͙ͬͱת͑Δɻ • ແବʹͳΔ͔΋͠Εͳ͍͕Ұ୴ςετίʔυΛॻ͘ • ॻ͍ͨςετίʔυͰ࢓༷Λຬ͍ͨͤͯΔ͔ෆ͕҆ͩفΔ • ϦϑΝΫλϦϯά͢Δ •

    ෆཁͳͬͨςετίʔυΛফ͢ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 26
  27. ςετίʔυ͕΄ͱΜͲͳ͍ ࣮͸ɺ͠͹Β͘खΛग़͢ͷ͕ڪΖͯ͘͠ɺΘ͔ͬͯ͸͍Δ΋ͷͷ ϦϑΝΫλϦϯά͸͍ͯ͠·ͤΜͰͨ͠ɻ ͳͷͰςετίʔυ΋ಉ༷ʹखग़͠Ͱ͖ͣɺ೔ʑͷӡ༻Ͱػೳ௥ Ճ΍վमͰͻͨ͢Βݕূͯ͠فΔΑ͏ʹσϓϩΠͯ͠·ͨ͠ɻ ͜ͷঢ়گΛվળ͢ΔͨΊͷࢼΈ͸ɺޙ൒Ͱ΋͏Ұ౓঺հ͠·͢ɻ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 27

  28. ʊਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓਓʊ ʼɹϩʔΧϧϑΝΠϧಡΈࠐΈ·͘Γɹʻ ʉY^Y^Y^Y^Y^Y^Y^YY^Y^Y^Y^Y^Y^Yʉ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 28

  29. ϩʔΧϧϑΝΠϧಡΈࠐΈ·͘Γ RedisΛϚελʔσʔλతͳ࢖͍͔ͨ͢ΔΑΓ͸શવྑ͍ʂ ͕ɾɾɾ͔͠͠ɻɻɻ ͦͷϩʔΧϧϑΝΠϧมߋස౓ߴ͍ͷͰɺϑΝΠϧ͚͍ͩͬͯ͡ deploy͢Δ͜ͱ͕ଟʑ͋Γɻ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 29

  30. ରॲޙ • ϩʔΧϧͱಉ͡ߏ੒ͷAmazon(S3͔ΒಡΈࠐΉΑ͏ʹमਖ਼ • ϑΥʔϧόοΫઌͱͯ͠ϩʔΧϧϑΝΠϧΛಡΈࠐΉʹमਖ਼ • ϑΥʔϧόοΫͯ͠Δ͜ͱΛairbrake౳ͷΤϥʔ௨஌αʔϏεʹ ௨஌ʢϥΠϒϥϦ͸(github.com/airbrake/gobrakeʣ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 30

  31. ରॲޙʢίʔυྫʣ // ٖࣅίʔυͰ͢ʢ͜ΜͳΠϝʔδʣ data, err := AmazonS3.Get(bucketName, filePath) if err

    != nil { // airbrake͢Δ data, err = Local.Get("filePath") if err != nil { // ී௨ʹerror } } @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 31
  32. ʊਓਓਓਓਓਓਓਓਓਓਓਓਓʊ ʼɹerrorΛѲΓͭͿͯ͠Δɹʻ ʉY^Y^Y^Y^Y^Y^Y^YY^Y^Y^ʉ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 32

  33. errorΛѲΓͭͿͯ͠Δ data, _ := json.Marshal(resp.Body) if data == nil {

    // ... } !!!!!!! @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 33
  34. ରॲޙ ී௨ʹΤϥʔϋϯυϦϯάɻ৔߹ʹΑͬͯ͸Airbrake౳Ͱ௨஌ɻ data, err := json.Marshal(resp.Body) if err != nil

    { return fmt.Errorf("XXXXͷjsonͷMarshalͰࣦഊͯ͠Δ %s", err) } if data == nil { // ... } @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 34
  35. ʊਓਓਓਓਓਓਓਓਓʊ ʼɹgorou%neཚ༻ɹʻ ʉY^Y^Y^Y^Y^Y^Y^Yʉ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 35

  36. gorou%neͷཚ༻ ฒߦॲཧͯ͠΋େৎ෉ͦ͏ͱ͍͏ཧ༝Ͱ ΊͪΌͪ͘Όgorou%neΛ࢖͍ͬͯΔɻ WaitGroupͱChannelΛ͋ͪͪ͜Ͱ౉ͯ͠ ࢖͍ͬͯͯɺͺͬͱݟԿ͍ͨ͠ͷ͔Θ͔ Β͵Ͱ͟͝ΔΑɾɾɾ ʢͦ΋ͦ΋ઃܭ͕͓͔͍͠ؾ͕͢Δʣ var wg sync.WaitGroup

    dataCh := make(chan string) for _, t := range hogeList { // ... লུ ... go hoge(dataCh, &wg) for _, c := range t.Contents { // ... লུ ... wg.Add(1) go func() { source(dataCh, c) wg.Done() }() } } wg.Wait() close(dataCh) @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 36
  37. ରॲʁޙ ຊ౰ʹύϑΥʔϚϯε͕ཁٻ͞ΕΔ࢓༷͔֬ೝͯ͠ɺҰ୴͸ී௨ ʹ૊ΜͰΈΑ͏ɻ ΋͠Ծʹੑೳཁ͕݅ຬͨͤͳ͍ͱͨ͠Βɺ ϕϯνϚʔΫΛͱ͔ͬͯΒ͜͏͍͏վम·ͨ͸ઃܭΛมߋ͢Δํ ޲Ͱɾɾɾͱ་͢ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 37

  38. ʊਓਓਓਓਓਓਓਓਓਓਓਓਓਓʊ ʼɹύοέʔδ෼͚೉͍͠໰୊ɹʻ ʉY^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Yʉ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 38

  39. ࣾ಺ڞ௨ϥΠϒϥϦతͳڞ௨ύοέʔδ APIͷϦϙδτϦͱ͸ผͰɺ$go get͍ͯͨࣾ͠಺GoϓϩδΣΫ τڞ௨ϥΠϒϥϦతͳ΋ͷɻʢͩͬͨ΋ͷ...ʣ ݁ߏAPIʹґଘͨ͠ίʔυ͕ଟ͘ɻ͜ͷϥΠϒϥϦ͚ͩΛվमͯ͠ ϦϦʔε$͜ͱ΋ଟʑ͋ͬͨɻ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 39

  40. ࣾ಺ڞ௨ϥΠϒϥϦతͳڞ௨ύοέʔδ ├── gocommon // ࣾ಺ڞ௨ϥΠϒϥϦͷϦϙδτϦ │ ├── airbrake // ࣾ಺GoϓϩδΣΫτͰڞ௨ͷϥΠϒϥϦ

    │ ├── aws │ ├── db │ ├── logger │ ├── xxxx // hoge-apiͰ͔͠࢖Θͳ͍Α͏ͳڞ௨ॲཧ܈ │ ├── yyyy │ └── zzzz └── hoge-api // apiͷϦϙδτϦʢgocommonόϦόϦ࢖͏ʣ ├── aaaa └── bbbb @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 40
  41. APIґଘ͍ͯ͠Δ΋ͷΛinternalԼʹҠಈͨ͠ ├── gocommon // ࣾ಺ڞ௨ϥΠϒϥϦͷϦϙδτϦ │ ├── airbrake // ࣾ಺GoϓϩδΣΫτͰڞ௨ͷϥΠϒϥϦ

    │ ├── aws │ ├── db │ └── logger └── hoge-api // apiͷϦϙδτϦ ├── aaaa // TODO: ͪ͜Β΋internal΁Ҡಈ༧ఆ ├── bbbb // TODO: ͪ͜Β΋internal΁Ҡಈ༧ఆ └── internal // apiͷڞ௨ϥΠϒϥϦ ├── xxxx ├── yyyy └── zzzz @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 41
  42. ΋ͬͱͨ͘͞Μ͋Γ·͕͢ɺ ࣌ؒ΋ͳ͘ͳΔ͠ң΋௧͘ͳΔͷͰ Ұ୴͜͜·ͰͰɾɾɾ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 42

  43. ͔͜͜Β͸ վળͷͨΊʹ΍ͬͨ͜ͱΛ঺հ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 43

  44. طଘϨΨγʔίʔυͷಈ࡞Λҡ࣋͢Δ WAFΛzenazn/goji*+>*guregu/kami$ʹ੾Γସ͑Δ࣌ʹ΍ͬͨ͜ͱ ʢϫʔΫΞϥ΢ϯυͰ͕͢ɾɾɾʣ • Өڹ͕େ͖ͦ͏/ṖͳAPI͸gojiͷHandler··ಈ͘Α͏ʹ͢Δ • x/net/contextͷத਎Λgojiͷweb.Cʹ͍ΕΔ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 44

  45. ιʔείʔυ func goji2kami(gojiFn web.HandlerFunc) kami.HandlerFunc { return func(ctx context.Context, w

    http.ResponseWriter, r *http.Request) { env := make(map[interface{}]interface{}) if di, ok := device.FromContext(ctx); ok { env["device"] = di } if u, ok := user.FromContext(ctx); ok { env["user"] = &models.User{ID: int(u.ID)} } else { env["user"] = (*models.User)(nil) } gojiFn(web.C{Env: env}, w, r) } } @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 45
  46. ϦϑΝΫλϦϯάDay ຖि༵ۚ೔ʹͻͨ͢ΒϦϑΝΫλϦϯά͢Δ೔ͱܾΊͨɻ աڈͷҨ࢈΍ྺ࢙తܦҢΛ౰࣌ͷϝϯόʔʹฉ͍ͨΓɺDBͷߏ଄ ΍࢑ఆાஔͷҋΛ෷ͬͨΓɻ • ෆཁͳίʔυΛ࡟আ • ςετ͕ແ͍Օॴͷςετ௥Ճ • ΉͪΌͪ͘ΌͳґଘΛ͍ͯ͠Δίʔυͷґଘؔ܎Λ੾Γ཭͢

    @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 46
  47. testॻ͍ͯͳ͍໰୊ ͦ΋ͦ΋ϨΨγʔͳίʔυ͕ଟ͗ͯ͢ॻ͘ؾྗ͕ى͖ͳ͍ͱ͔ ʢΘ͔Βͳ͘΋ͳ͍͕ɺ͜ͷ··Ͱ͸·͍ͣɾɾɾʣ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 47

  48. testॻ͍ͯͳ͍໰୊ͱ޲͖߹ͬͨ • CircleCIͰࣗಈςετ • CoverallsͰΧόϨοδΛՄࢹԽ-! • શͯͷΧόϨοδΛऔΔͷʹgithub.com/pierrre/gotestcoverΛ ར༻ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 48

  49. ΧόϨοδͷվળ ΧόϨοδ20%$%>$50%෇ۙ΁ ͻͱ·ͣɺtestΛॻ͘จԽΛ࡞Δ͜ͱʹ੒ޭ!❗!❗!❗!❗!❗ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 49

  50. gomockΛಋೖ ϦϑΝΫλϦϯάͷࡍʹɺґଘؔ܎Λഉআ͢ΔͨΊinterfaceԽ͠ ͯgomock͕࢖͑ΔΑ͏ʹͳͬͨͷͰಋೖɻ @tenntenn͞ΜͷQiitaهࣄ͕ͱͯ΋෼͔Γ΍͍͢Ͱ͢ɻ Go#MockͰΠϯλϑΣʔεͷϞοΫΛ࡞ͬͯςετ͢Δ h(p:/ /qiita.com/tenntenn/items/24fc34ec0c31f6474e6d @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 50

  51. hhthͷಋೖ Go͸testॻ͘ͷָ͚ͩͲɺAPIͷςετΛॻ͘ͷ͕ͪΐͬͱΊΜͲ ͏ɻɻɻ h*ptest࢖ͬͯ݁ߏΰϦΰϦίʔυॻ͔ͳ͍ͱ͍͚ͳ͍ɻ github.com/kyokomi/hhth.Λ࡞ͬͨɻ ※·ͩҰ෦Ͱ͔͠࢖͍ͬͯͳ͍&·ͩ·ͩվળͷ༨஍͕͋Δ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 51

  52. hhthಛ௃ h"ptestͷhelperΫϥεతͳҐஔ෇͚ɻ h"ptestͰAPIΛݺͼग़ͨ݁͠ՌͷνΣοΫॲཧΛ࢖͍·ΘͤΔɻ • StatusCode • ContentLength • jsonͷParse •

    Response @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 52
  53. hhth#࢖͍ํྫ ʢ͕࣌ؒ͋Ε͹ɾɾɾʣ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 53

  54. package example_test import ( "net/http" "testing" "github.com/kyokomi/hhth" ) func TestHogeHandler(t

    *testing.T) { hhtHelper := hhth.New(http.DefaultServeMux) hhtHelper.SetTestCase( hhth.TestCaseStatusCode(http.StatusOK), hhth.TestCaseContentType("text/plain; charset=utf-8"), ) resp := hhtHelper.Get("/hoge") if resp.Error() != nil { t.Errorf("error %s", resp.Error()) } if resp.String() != "hoge" { t.Errorf("error response body hoge != %s", resp.String()) } } @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 54
  55. ࢖͍ํৄࡉ1 // HanderΛηοτͯ͠એݴ hhtHelper := hhth.New(http.DefaultServeMux) // TestCaseΛઃఆʢStatusCodeͱContentTypeͷνΣοΫʣ hhtHelper.SetTestCase( hhth.TestCaseStatusCode(http.StatusOK),

    hhth.TestCaseContentType("text/plain; charset=utf-8"), ) @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 55
  56. ࢖͍ํৄࡉ2 // GET /hogeΛςετ͢Δ resp := hhtHelper.Get("/hoge") // ઌ΄Ͳઃఆͨ͠handlerΛݺͼग़ͨ݁͠Ռ or

    TestCaseͷΤϥʔ͕ฦͬͯ͘Δ if resp.Error() != nil { t.Errorf("error %s", resp.Error()) } @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 56
  57. ࢖͍ํৄࡉ3 // CustomTestCaseΛ௥ՃʢJSONΛύʔεͯ͠த਎ΛνΣοΫʣ hhtHelper.Get("/hoge", func(resp hhth.Response) error { var hoge

    Hoge if err := resp.JSON(&hoge); err != nil { return fmt.Errorf("hoge parse error %s", err.Error()) } if hoge.Num() != 100 { return fmt.Errorf("hoge num 100 != %d", hoge.Num()) } return nil }) @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 57
  58. ·ͱΊ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 58

  59. ·ͱΊ 1. GoݴޠͰ΋ϨΨγʔίʔυ͕؆୯ʹੜ·Ε·͢ %>'࢓༷΍ઃܭ͸ͪΌΜͱݕ౼ͯ͠·ͱΊΑ͏ 2. ςετίʔυ͕ແ͍ͱʮ͔ΘΓʹفΔ͕࣌ؒ૿͑ͨʯͱ͔ݴ͍ ͩ͠·͢ %>'·ͣ͸ςετίʔυΛͪΌΜͱॻ͜͏ %>'ͦͯ͠ܧଓతʹϦϑΝΫλϦϯά͍ͯ͘͠จԽʹ͠Α͏ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ

    59
  60. ͓ΘΓ @kyokomi,(2015/12/06(ʮGo(Conference(2015(winterʯ 60