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

テストを書かない言い訳をした結果②-負債にならないテストを求めて- / 2170726 #ios_test_night

takasek
July 26, 2017

テストを書かない言い訳をした結果②-負債にならないテストを求めて- / 2170726 #ios_test_night

iOS Test Night #5 - connpass
https://testnight.connpass.com/event/59283/
の発表資料です

---

# 参考リンク

takasek/XCJumpToTests: the script to jump files of Implementation⇄Tests
https://github.com/takasek/XCJumpToTests

typelift/SwiftCheck: QuickCheck for Swift
https://github.com/typelift/SwiftCheck

Property-Based Testing with SwiftCheck
https://news.realm.io/news/tryswift-tj-usiyan-property-based-testing-swiftcheck/

takasek

July 26, 2017
Tweet

More Decks by takasek

Other Decks in Programming

Transcript

  1. ςετΛॻ͔ͳ͍
    ݴ͍༁Λͨ݁͠Ռᶄ
    -ෛ࠴ʹͳΒͳ͍ςετΛٻΊͯ-
    by.
    2017/7/26 iOS Test Night #5
    1

    View Slide

  2. takasek
    iOS Developer
    @takasek
    OSS
    ActionClosurable
    Notifwift౳
    2

    View Slide

  3. લճ·Ͱͷ
    ͋Β͢͡
    3

    View Slide

  4. iOS Test Night #3 ʹͯൃද
    4

    View Slide

  5. 5

    View Slide

  6. ςετॻ͖ͨ
    ͘ͳ͍ཧ༝ᶃ
    ςετϑΝΠϧ΁
    ͷڑ཭͕ԕ͍
    6

    View Slide

  7. ଓฤൃදͰղܾ
    (iOS Test Night #4)
    7

    View Slide

  8. ςετϑΝΠϧ΁ͷڑ཭͕ԕ͍ͷͳΒ
    ςετͱ࣮૷ͷڑ཭Λ
    ͚ۙͮΔεΫϦϓτΛ
    ࡞Δ!
    8

    View Slide

  9. https://github.com/takasek/XCJumpToTests
    9

    View Slide

  10. ͜ΕͰ࣮૷ϑΝΠϧͱςετϑΝΠϧΛҰൃͰ੾Γସ͑ΒΕΔʂ
    10

    View Slide

  11. ৄ͘͠͸લճൃදͷSpeakerDeckͰ
    https://speakerdeck.com/takasek/tesutowoshu-kanaiyan-iyi-wositajie-
    guo-wwwwwww
    11

    View Slide

  12. !
    12

    View Slide

  13. ςετॻ͖ͨ͘ͳ͍ཧ༝ᶄ
    ෛ࠴ʹͳΓ΍͍͢
    "The dependencies between packages should be in the direction of the
    stability of the packages. A package should only depend upon packages
    that are more stable than it is. " 1
    ύοέʔδͷґଘؔ܎͸ɺ҆ఆੑͷํ޲ʹґΒͳ͚Ε͹ͳΒͳ͍ɻ
    ύοέʔδ͸ɺΑΓ҆ఆͨ͠ύοέʔδʹͷΈґଘ͢΂͖ͩɻ
    ɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹ- Robert C. Martin
    1
    Stable Dependencies Principle
    http://wiki.c2.com/?StableDependenciesPrinciple
    13

    View Slide

  14. 14

    View Slide

  15. ࠓճ͸
    ͪ͜Βʹ͍ͭͯൃද͠·͢
    15

    View Slide

  16. ڭ͑ͯ΋Βͬͨ
    "ςετ͕ෛ࠴ʹͳΓ΍͍͢"
    ໰୊΁ͷղ
    4 doctest
    4 ϓϩύςΟϕʔεͷςετ
    16

    View Slide

  17. doctest
    17

    View Slide

  18. doctest ͱ͸ 2
    doctest Ϟδϡʔϧ͸ɺର࿩త Python ηογϣϯͷΑ͏ʹݟ͑ΔςΩετΛ୳͠ग़͠ɺη
    ογϣϯͷ಺༰Λ࣮ߦͯ͠ɺͦ͜ʹॻ͔Ε͍ͯΔ௨Γʹৼ෣͏͔Λௐ΂·͢ɻ doctest ͸Ҏ
    ԼͷΑ͏ͳ༻్ʹΑ͘࢖ΘΕ͍ͯ·͢:
    4 Ϟδϡʔϧͷ docstring (υΩϡϝϯςʔγϣϯจࣈྻ) தʹ͋Δର࿩࣮ߦྫͷ͢΂͕ͯ
    ॻ͔Ε͍ͯΔ௨Γʹಈ࡞͢Δ͔ݕূ͢Δ͜ͱͰɺdocstring ͷ಺༰͕࠷৽͔Ͳ͏͔νΣο
    Ϋ͢Δɻ
    4 ςετϑΝΠϧ΍ςετΦϒδΣΫτதͷର࿩࣮ߦྫ͕ظ଴௨Γʹಈ࡞͢Δ͔Λݕূ͢Δ
    ͜ͱͰɺճؼςετΛ࣮ݱ͠·͢ɻ
    4 ೖग़ྗྫΛ๛෋ʹ࢖ͬͨύοέʔδͷνϡʔτϦΞϧυΩϡϝϯτ͕ॻ͚·͢ɻೖग़ྗྫ
    ͱղઆจͷͲͪΒʹ஫໨͢Δ͔ʹΑͬͯɺυΩϡϝϯτ͸ʮಡΊΔςετʯʹ΋ʮ࣮ߦͰ
    ͖ΔυΩϡϝϯτʯʹ΋ͳΓ·͢ɻ
    2
    https://docs.python.jp/3/library/doctest.html
    18

    View Slide

  19. doctest αϯϓϧίʔυ(python)
    def hoge():
    """
    >>> hoge()
    'Hello World'
    """
    return "Hello world"
    if __name__ == "__main__":
    import doctest
    doctest.testmod()
    19

    View Slide

  20. doctest ࣮ߦ݁Ռ
    [demo] % python example.py
    **********************************************************************
    File "example.py", line 4, in __main__.hoge
    Failed example:
    hoge()
    Expected:
    'Hello World'
    Got:
    'Hello world'
    **********************************************************************
    1 items had failures:
    1 of 1 in __main__.hoge
    ***Test Failed*** 1 failures.
    20

    View Slide

  21. doctest ݕূ݁Ռ
    def hoge():
    """
    >>> hoge()
    'Hello World'
    """
    return "Hello world"ɹ! w ͕খจࣈʹͳͬͯΔʂʂ
    if __name__ == "__main__":
    import doctest
    doctest.testmod()
    21

    View Slide

  22. ର࿩؀ڥΛ
    ͦͷ··ίϐϖͨ͠Β
    ςετʹͳΔͷ
    ͍͍Ͷ !
    22

    View Slide

  23. OSSͰSwift༻ͷ
    doctestπʔϧ
    ͳ͍͔ͳ…ʁ
    23

    View Slide

  24. https://github.com/x007th/SwiftDocTest
    ! Starͷ਺ɺ3೥લ͔Βߋ৽͕ࢭ·ͬͯΔɺ౳ ؾʹͳΔ…
    24

    View Slide

  25. SwiftDocTest ͸͜Μͳײ͡Ͱ࢖͏Β͍͠!
    /**
    :test: floor(CGPoint(x:10.9, y:-10.5))
    :result: CGPoint(x:10, y:-11)
    */
    public func floor(value:CGPoint) -> CGPoint {
    return value.map(floor)
    }
    /**
    :test: ceil(CGPoint(x:10.9, y:-10.5))
    :result: CGPoint(x:11, y:-10)
    */
    public func ceil(value:CGPoint) -> CGPoint {
    return value.map(ceil)
    }
    ίϐϖͮ͠Βͦ͏…
    25

    View Slide

  26. Swift/Xcode΋
    doctest తͳ΋ͷ
    ಋೖͯ͘͠Εͯ΋
    ͍͍ͷͰ͸
    26

    View Slide

  27. Swift/Xcode΋
    doctest తͳ΋ͷ
    ಋೖͯ͘͠Εͯ΋
    ͍͍ͷͰ͸
    document comments
    ॆ࣮ͤ͞Α͏ͬͯ
    API guideline Ͱ΋
    ݴͬͯΔΜͩ͠
    27

    View Slide

  28. !
    28

    View Slide

  29. ϓϩύςΟϕʔεͷ
    ςετ
    (QuickCheck)
    29

    View Slide

  30. QuickCheck ͱ͸ 3
    QuickCheckʢΫΠοΫνΣοΫʣ͸ɺ(...) HaskellͰॻ͔Εͨί
    ϯϏωʔλϥΠϒϥϦͰ͋Δɻ
    QuickCheckͰ͸ϓϩάϥϚ͸ؔ਺͕ຬͨ͢΂͖࿦ཧతੑ࣭ΛΞα
    ʔγϣϯͱͯ͠ॻ͘ɻςετ͸ؔ਺ͷҾ਺ͷܕ͕औΓಘΔ஋Λϥϯ
    μϜʹؔ਺ʹ༩͑Δ͜ͱͰɺΞαʔγϣϯ͕ࣦഊ͢Δ৚݅Λ୳ͦ͏
    ͱࢼΈΔɻ(...) QuickCheck͸௨ৗͷϓϩάϥϜͷςετʹՃ͑ɺ
    ؔ਺࢓༷ͷࡦఆɺؔ਺͕͢Δ΂͖ڍಈΛυΩϡϝϯτͱͯࣔ͢͠ɺ
    ίϯύΠϥͷ࣮૷Λςετ͢Δɺͱ͍ͬͨ͜ͱʹ΋༗༻Ͱ͋Δɻ
    3
    https://ja.wikipedia.org/wiki/QuickCheck
    30

    View Slide

  31. https://github.com/typelift/SwiftCheck
    Star΋ଟ͘ɺܧଓͯ͠։ൃ͞Ε͍ͯΔɻΑͦ͞͏ɻ
    31

    View Slide

  32. SwiftCheck ͷ࢖͍ํ
    ϥϯμϜͳ஋͕ Generate Ͱ͖ΔͷͰ…
    32

    View Slide

  33. ૊Έ߹ΘͤͯδΣωϨʔλΛ࡞ͬͯ…
    let localEmail = allowedLocalCharacters
    .proliferateNonEmpty
    .suchThat({ $0[$0.index(before: $0.endIndex)] != "." })
    .map { String($0) }
    let hostname = Gen.one(of: [
    lowerCaseLetters,
    numeric,
    Gen.pure("-"),
    ]).proliferateNonEmpty.map { String($0) }
    let tld = lowerCaseLetters
    .proliferateNonEmpty
    .suchThat({ $0.count > 1 })
    .map { String($0) }
    let emailGen = glue([
    localEmail,
    Gen.pure("@"),
    hostname,
    Gen.pure("."),
    tld
    ])
    emailGen.generate // "q13vS#@s409pwr-plv90-w40--91-.ijtgmvoiljnticlggqlpw"
    emailGen.generate // "!'[email protected]"
    emailGen.generate // "46n}ZmEDg760&4L4RR#.Q'b{}[email protected]"
    33

    View Slide

  34. ςετσʔλΛੜ੒͢ΔArbitraryΛ༻ҙͯ͠
    struct ArbitraryEmail : Arbitrary {
    let email : String
    static var arbitrary : Gen {
    return emailGen.map(ArbitraryEmail.init)
    }
    }
    34

    View Slide

  35. ͦͷੑ࣭ΛνΣοΫ
    property("τοϓϨϕϧυϝΠϯͷμϝͳϝΞυ")
    <- forAll { (arbitrary : ArbitraryEmail) in
    ! arbitrary.email.contains(".")
    }.expectFailure
    // +++ OK, failed as expected. Proposition: τοϓϨϕϧυϝΠϯͷͳ͍ϝΞυ
    // Falsifiable (after 1 test):
    // ArbitraryEmail(email: "{@3.ae")
    35

    View Slide

  36. ςετσʔλ͕
    ൒ࣗಈͰ࡞ΒΕΔͷ
    ͍͍Ͷ !
    36

    View Slide

  37. ΋ͬͱৄ͘͠ Property-Based Testing
    https://news.realm.io/news/tryswift-tj-usiyan-property-based-testing-swiftcheck/
    37

    View Slide

  38. ࠓ೔ͷ·ͱΊ
    ςετΛॻ͔ͳ͍ݴ͍༁Λ
    ͨ݁͠Ռ…
    38

    View Slide

  39. ࠓ೔ͷ·ͱΊ
    ςετΛॻ͔ͳ͍ݴ͍༁Λ
    ͨ݁͠Ռ…
    ෛ࠴ʹͳΓʹ͍͘ςετํ
    ๏ͷ஌ݟ͕ू·ͬͨ !
    39

    View Slide