Upgrade to Pro — share decks privately, control downloads, hide ads and more …

カバレッジ95%以上を実現した テストコードの書き方

yudppp
April 21, 2018

カバレッジ95%以上を実現した テストコードの書き方

Battle Conference U30 ( https://bcu30.jp/ )で話した時の資料です

より安心して使っていただくために、これまでどうしても超えられなかった95%の壁を越えるための挑戦についてと今後のテスト方針について

yudppp

April 21, 2018
Tweet

More Decks by yudppp

Other Decks in Technology

Transcript

  1. ΧόϨοδ95%Ҏ্Λ࣮ݱͨ͠
    ςετίʔυͷॻ͖ํ
    גࣜձࣾ)3#SBJO

    ླ໦༔େ

    View full-size slide

  2. ࣗݾ঺հ
    w ླ໦༔େ
    w גࣜձࣾ)3#SBJOॴଐ
    w ޷͖ͳݴ༿ႈ౳ੑ
    w (P+BWB4DSJQU

    View full-size slide

  3. -ैۀһͷ໨ඪΛ؅ཧͯ͠
    ɹɹɹɹධՁ͢ΔͨΊͷΫϥ΢υπʔϧ-
    https://okr.hrbrain.biz/

    View full-size slide

  4. ૣ଎Ͱ͕͢ɺ

    View full-size slide

  5. Έͳ͞Μςετ͸޷͖Ͱ͔͢?

    View full-size slide

  6. ࢲ͸ݏ͍Ͱ͢

    View full-size slide

  7. ςετΛݏ͍ͳཧ༝
    w ΊΜͲ͍͘͞
    w ଞਓͷॻ͍ͨςετίʔυΛϝϯςφϯε͢ΔͷͭΒ͍
    w ࣮ߦ͢Δͷʹ͕͔͔࣌ؒΔ

    View full-size slide

  8. ࠓ·ͰͷγεςϜͱςετίʔυ

    View full-size slide

  9. γεςϜ֓ཁ
    w (P
    w 3%#
    w .7$෩ͷΞʔΩςΫνϟ

    View full-size slide

  10. DBʹ௚઀ΞΫηε͢Δςετ(ControllerͰ΋)
    func TestXxx(t *testing.T) {
    // DBʹςετσʔλΛηοτ͢Δ
    // ͍͔ͭ͘ͷςετέʔεͰςετ͢Δ
    // ςετσʔλͷ࡟আ
    }

    View full-size slide

  11. ಉ࣌ʹ࣮ߦ͢Δͱࣦഊ͢Δςετ
    w ςετσʔλ͕ڝ߹͠λΠϛϯάʹΑͬͯ͸༧ظͤ͵σʔλ͕ฦ
    Γςετ͕ࣦഊ͢Δɻɻ

    View full-size slide

  12. ղܾํ๏
    go test -parallel 1 ./...

    View full-size slide

  13. ଞͷਓ͕ॻ͍ͨςετ
    w ਆݒ͔ΓతͳόϥϯεͰ੒Γཱ͍ͬͯΔ
    w $POUSPMMFSʹۙͮ͘΄Ͳෳࡶ͞͸૿͢
    w ΧόϨοδΛݟΔݶΓςετࣗମ͸໰୊ͳ͘͞Ε͍ͯͦ͏
    w ผػೳ͕ϦϦʔε͞Εͨͱ͖ʹσάϨ͞Ε͍ͯͳ͍͜ͱͷ֬ೝ͸
    Ͱ͖͍ͯΔ͕ɺؔ܎ͨ͠ػೳΛϦϦʔε͍ͨ͠ͱ͖ʹਏ͍ɻ

    View full-size slide

  14. ౰࣌ͷϥΠϯΧόϨοδ
    w ϛυϧ΢ΣΞͳͲେࣄͳͱ͜ΖͰ΋ఔ౓
    w શମݟΔͱΛ੾͍ͬͯͨ
    w ΧόϨοδΛ্͛Α͏ͱࢥͬͯ΋େมͳঢ়ଶ

    View full-size slide

  15. मਖ਼ޙ
    (Ұ෦·࣮ͩ૷தͷ΋ͷ͕͋Γ·͢)

    View full-size slide

  16. ࣮૷࣌ͷϧʔϧΛ͍͔ͭ͘࡞Γ·ͨ͠

    View full-size slide

  17. ΫϦʔϯΞʔΩςΫνϟ
    https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

    View full-size slide

  18. ΫϦʔϯΞʔΩςΫνϟ
    w ؔ৺ͷ෼཭
    w *OUFSGBDFͷಠཱ
    w σʔλϕʔεͷಠཱ
    w ֎෦ػೳͷಠཱ

    View full-size slide

  19. σΟϨΫτϦߏ੒
    .
    ├── application
    │ └── usecase - usecase
    ├── domain
    │ ├── entity - domain entity(simple struct only)
    │ ├── repository - domain repository(interface only)
    │ └── service - domain service
    ├── infrastructure
    │ └── persistence
    │ └── rdb - implement domain repository
    └── interfaces
    └── api
    ├── internal
    │ ├── handler - http handler
    │ ├── http_utils - http utils
    │ └── middleware - http server middleware
    └── server.go - http server endpoint

    View full-size slide

  20. DI͢Δ
    type UserRepository interface {
    GetByID(ctx context.Context, id uint64) (*User, error)
    }

    type userService struct {
    repo UserRepository
    }
    func NewUserService(repo UserRepository) UserService {
    return &userService{
    repo: repo,
    }
    }


    func (u *userService) GetByID(ctx context.Context, id uint64) (*User, error) {

    }

    View full-size slide

  21. ϞοΫԽͯ͠ςετ
    import (
    "context"
    "testing"
    "github.com/golang/mock/gomock"
    ...
    )
    func TestUserServiceGetByID(t *testing.T) {
    ...
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()
    mockRepo := mock.NewMockUserRepository(ctrl)
    mockRepo.EXPECT().GetByID(c.context, c.id).Return(c.mockuser, nil)
    service := NewUserService(mockRepo)
    actual, err := GetByID(c.context, c.id)
    ...
    }

    View full-size slide

  22. SQL΋ϞοΫԽ
    import (
    "regexp"

    sqlmock "gopkg.in/DATA-DOG/go-sqlmock.v1"
    )
    func TestGetByID(t *testing.T) {

    query := `SELECT …`
    rows := sqlmock.NewRows(columns)
    rows = rows.AddRow(expextRow…)
    mock.ExpectQuery(query).WillReturnRows(rows)

    repo := NewUserRepository(mockDB)
    actual, err := repo.GetByID(c.context, c.id)

    }

    View full-size slide

  23. TableDrivenTests
    func TestSquare(t *testing.T) {
    cases := []struct{
    expect int
    input int

    }{{1,1},{4,2}}
    for _, c := range cases {
    actual := Square(c.input)
    if actual != c.expect {
    t.Errorf("Square(%q) == %q, want %q",

    c. input, actual, c.expect,
    )
    }
    }
    }

    View full-size slide

  24. ͲͪΒͷํ͕ྑ͍ίʔυͰ͠ΐ͏͔
    if (A || B || C) {
    return errors.New("something")
    }
    return nil
    if (A) {
    return errors.New("something1")
    }
    if (B) {
    return errors.New("something2")
    }
    if (C) {
    return errors.New("something3")
    }
    return nil

    View full-size slide

  25. ӈΛྑ͍ͱ͍ͯ͠·͢
    if (A || B || C) {
    return errors.New("something")
    }
    return nil
    if (A) {
    return errors.New("something1")
    }
    if (B) {
    return errors.New("something2")
    }
    if (C) {
    return errors.New("something3")
    }
    return nil

    View full-size slide

  26. ϥΠϯΧόϨοδͩͱͲͷέʔε͕ςετ
    ͞Ε͍ͯΔ͔Θ͔Βͳ͍
    ͦ΋ͦ΋Τϥʔ͸෼͚Δ΂͖

    View full-size slide

  27. ϥΠϯΧόϨοδ
    $ go test -cover `go list ./... | grep -v mock`
    ok .../application/usecase coverage: 97.8% of statements
    ? .../domain/entity [no test files]
    ? .../domain/repository [no test files]
    ok .../domain/service coverage: 96.2% of statements
    ok …/infrastructure/persistence/rdb coverage: 100.0% of statements
    ok .../interfaces/api/internal/handler coverage: 94.7% of statements
    ok .../interfaces/api/internal/middleware coverage: 95.1% of statements

    View full-size slide

  28. ࢒Γͷ5%ʹ͍ͭͯ
    cookie, err := r.Cookie("cookie_name")
    if err != nil {
    if err == http.ErrNoCookie {
    h.ServeHTTP(w, r)
    return
    }
    w.WriteHeader(http.StatusInternalServerError)
    return
    }

    View full-size slide

  29. ςετΛݏ͍ͳཧ༝
    w ΊΜͲ͍͘͞

    ˠΧόϨοδΛ͋͛Δୡ੒ײΛָ͍͠ͱࢥ͍͜·ͤΔɻ
    w ଞਓͷॻ͍ͨςετίʔυΛϝϯςφϯε͢ΔͷͭΒ͍

    ˠϞοΫԽ͢Δ͜ͱͰγϯϓϧͳςετίʔυ΁
    w ࣮ߦ͢Δͷʹ͕͔͔࣌ؒΔ

    ˠίΞ਺෼ฒྻʹ࣮ߦͰ͖ΔΑ͏ʹͳͬͨɻ

    ɹʴ3%#ʹ઀ଓ͠ͳ͍෼ଟগ଎͘ͳͬͨɻ

    View full-size slide

  30. (͜ͷลΓ͔Βເͷ࿩)

    View full-size slide

  31. ϑϩϯτΤϯυฤ

    View full-size slide

  32. ී௨ʹϢχοτςετΛ
    ͍ͬͺ͍ॻ͖͍ͨ

    View full-size slide

  33. ݱঢ়
    w εϓϨουγʔτͰ؅ཧ͞Ε͍ͯΔςετ߲໨ॻΛ΋ͱʹखಈͰ
    ςετ
    w ͲΜͲΜػೳ͕૿͖͑ͯͯɺςετ߲໨΋૿͍͖͑ͯखಈ͕ਏ͘
    ͳ͖ͬͯͨ

    View full-size slide

  34. ࣗಈԽ͍ͨ͠
    ?

    View full-size slide

  35. Puppeteer
    w $ISPNF%FW5PPMT։ൃνʔϜ͕ग़ͨ͠044
    w )FBEMFTT$ISPNF
    w $ISPNFͷΈ͔͠ಈ͔ͳ͍
    w /PEF+4Ͱಈ͘
    w IUUQTHJUIVCDPN(PPHMF$ISPNFQVQQFUFFS
    w IUUQTUSZQVQQFUFFSBQQTQPUDPN

    View full-size slide

  36. Ͳ͏ͯ͠SeleniumΛ࢖Θͳ͍ͷ͔
    w ಋೖ͕؆୯
    w /PEF+4Ͱςετ͕ॻ͖΍͍͢ "1*͕Θ͔Γ΍͍͢

    w ໨తͱͯ͠͸σάϨͷ๷ࢭ͕΄ͱΜͲͰ$ISPNF͚ͩͰ֬ೝͰ͖
    Ε͹Α͍ ܦݧ্σάϨͰ*&Ͱಈ͔ͳ͘ͳͬͨέʔε͸͋·Γͳ
    ͍

    View full-size slide

  37. ࣗಈԽ͕؆୯ʹͰ͖Δͱ͜ΖͷΈࣗಈԽ
    ͦͷ͏ͪશͯࣗಈԽ͍ͨ͠(ເ)͕ແཧ͸͠ͳ͍

    View full-size slide

  38. ੬ऑੑςετ

    View full-size slide

  39. ݱঢ়
    w ఆظతʹ֎෦ʹ੬ऑੑςετΛ࣮ࢪ
    w ಺෦Ͱ΋खಈͰ࣮ࢪ

    View full-size slide

  40. શͯͷϦϦʔε͝ͱʹ੬ऑੑςετ(ເ)

    View full-size slide

  41. VAddy
    w खܰͰߴ଎ͳΫϥ΢υܕ8FC੬ऑੑݕࠪπʔϧ
    w ༧Ί༻ҙͨ͠63-΍ύϥϝʔλΛݩʹ੬ऑੑݕࠪΛߦ͏
    w 42-ΠϯδΣΫγϣϯ944ϦϞʔτϑΝΠϧΠϯΫϧʔδϣϯ
    ίϚϯυΠϯδΣΫγϣϯσΟϨΫτϦτϥόʔαϧ

    View full-size slide

  42. VAddy
    ༧Ί༻ҙͨ͠URL:

    - http://example.com/search.php?keyword1=foo&keyword2=bar

    ΞΫηε͞ΕΔURL:

    - http://example.com/search.php?keyword1=foo&keyword2=ba
    - http://example.com/search.php?
    keyword1=foovaddy&keyword2=bar
    ...

    View full-size slide

  43. ༧Ί༻ҙ͞ΕͨURL΍ύϥϝʔλ?

    View full-size slide

  44. ༻ҙ͠ͳͯ͘΋͍͍ΜͰ͢

    View full-size slide

  45. ςετ͢ΔURLͷऩू
    CI
    Application
    Proxy
    E2E

    View full-size slide

  46. ऩूͨ͠URLΛݩʹ੬ऑੑςετ
    CI
    Application
    ੬ऑੑ਍அ

    View full-size slide

  47. E2Eςετ͕ॻ͔Ε͍ͯΕ͹
    CIͰ׬݁͞ΕΔ

    View full-size slide

  48. ࣗಈͰϦϦʔε͝ͱʹ੬ऑੑ਍அͰ͖Δͷ͸
    ΍͹͍ͷͰͳΔ΂͘ૣ͘΍Γ͍ͨ

    View full-size slide

  49. ิࠤతͳ΋ͷͳͷͰ
    ࠓޙ΋֎෦ʹґཔͨ͠Γɺ
    ࣗ෼ͨͪͰ֬ೝͨ͠Γ͸͠·͢

    View full-size slide

  50. ·ͱΊ
    w 5FTUBCMFͳߏ੒ʹ͢΂͖ɻ
    w ·ͨ5FTUBCMFͳίʔυ͸ݟ΍͘͢ศར
    w 1VQQFUFFSͱ7"EEZΛ࢖ͬͯɺΑΓ҆৺ͳαʔϏε΁͍͖ͯ͠·
    ͢ɻ

    View full-size slide

  51. ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ

    View full-size slide