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

2019年のSwiftモック事情

Sho Ikeda
December 16, 2019

 2019年のSwiftモック事情

「年末だよ Android/iOS Test Night - 2019」での発表資料です #test_night

https://testnight.connpass.com/event/155429/

Sho Ikeda

December 16, 2019
Tweet

More Decks by Sho Ikeda

Other Decks in Programming

Transcript

  1. 2019೥ͷ
    SwiftϞοΫࣄ৘
    Sho Ikeda / @ikesyo
    ೥຤ͩΑ Android/iOS Test Night - 2019
    2019-12-16 Mon
    #test_night

    View full-size slide

  2. Sho Ikeda / @ikesyo
    • ͍͚͠ΐʔʗ஑ా ᠳ
    • ͸ͯͳ@ژ౎
    • εϚʔτϑΥϯΞϓϦ։ൃ
    • iOS / Android / React Native
    • Swiftίϛολʔ
    • swift-corelibs-foundation
    • Quick/Nimbleͷϝϯςφʔ
    • https://twitter.com/ikesyo
    • https://github.com/ikesyo

    View full-size slide

  3. ͋ΘͤͯಡΈ͍ͨ
    • Swiftʹ͓͚ΔMockϥΠϒϥϦͷ׆
    ༻/swift-mock-library
    • https://speakerdeck.com/
    yusukehosonuma/swift-mock-
    library

    View full-size slide

  4. How to Mock Protocols in Swift
    • Manually
    !
    • Sourcery: AutoMockable
    • SwiftyMocky
    • Cuckoo
    • Mockolo <-
    "❗

    View full-size slide

  5. Sourcery:
    AutoMockable

    View full-size slide

  6. Sourcery
    • https://github.com/krzysztofzablocki/Sourcery
    • Swift༻ͷ൚༻తͳίʔυδΣωϨʔλʔ
    • ιʔείʔυͷղੳɾΧελϜΞϊςʔγϣϯ: SourceKitten
    • ςϯϓϨʔτγεςϜ: Stencil
    • try! Swift Tokyo 2018 - Investing time into developer tools
    and experience
    • https://www.youtube.com/watch?v=yAQQ0cIxSF8

    View full-size slide

  7. Sourcery: AutoMockable
    • ϞοΫ༻ͷςϯϓϨʔτ
    • Mocks.md
    • AutoMockable.stencil
    • ର৅ͱͳΔͷ͸
    • AutoMockable ͱ͍͏໊લͷϓϩτίϧʹ४ڌ
    • // sourcery: AutoMockable ͷΞϊςʔγϣϯίϝϯτͷ෇

    View full-size slide

  8. Sourcery: AutoMockable
    • ϝιου
    • ݺ͹Ε͔ͨͲ͏͔ͷνΣοΫ: ճ਺͸෼͔Βͳ͍
    • Ҿ਺λϓϧͷϓϩύςΟ: ݺ͹ΕͨҾ਺ͷνΣοΫ
    • ໭Γ஋ઃఆ༻ͷϓϩύςΟ
    • ϓϩύςΟఆٛ
    • ͔ͳΓγϯϓϧ
    • ֎෦Ҿ਺໊͕ಉҰɾܕҧ͍ͷΦʔόʔϩʔυ͸NG
    • ϓϩτίϧఆٛதʹ֎෦ϑϨʔϜϫʔΫͷܕ͕ొ৔͢Δ৔߹ɺࣗ෼ͰςϯϓϨʔτΛมߋ
    ͯ͠importΛॻ͖Ճ͑Δඞཁ͕͋Δ

    View full-size slide

  9. SwiftyMocky
    • https://github.com/MakeAWishFoundation/SwiftyMocky
    • Sourceryϕʔε
    • AutoMockableͷϚʔΫΛͦͷ··࢖༻
    • ΑΓߴػೳͳςϯϓϨʔτͱϥϯλΠϜ
    • SourceryͷAutoMockableςϯϓϨʔτ͔ΒҠߦ͠΍͍͢
    • Mockfileͱ͍͏ઃఆϑΝΠϧͱɺswiftymockyͱ͍͏ϥούʔί
    Ϛϯυ͕༻ҙ͞Ε͍ͯΔ

    View full-size slide

  10. SwiftyMocky
    • ϓϩτίϧͷϞοΫ

    • ϓϩτίϧܧঝ

    • ΫϥεͷϞοΫ

    • δΣωϦΫε

    • Handling Generics.md
    • staticϝϯόʔ

    • Objective-Cαϙʔτʢ@objcϓϩτίϧʣ

    • ϑΝΠϧɾσΟϨΫτϦ୯ҐͰιʔεΛࢦ

    • ઃఆϑΝΠϧͰimport͢ΔϑϨʔϜϫʔΫ
    ΛࢦఆՄೳ
    • swiftymocky autoimportίϚϯυͰࣗ
    ಈੜ੒Մೳ
    • GivenʹΑΔελϒʢ໭Γ஋ͷࢦఆʣ
    • VerifyʹΑΔϝιουݺͼग़͠ͷݕࠪ
    • ճ਺΍Ҿ਺
    • PerformʹΑΔ೚ҙͷΫϩʔδϟͷ࣮ߦ

    View full-size slide

  11. SwiftyMocky: Given
    Given(mock, .surname(for name: .value("Johnny"), willReturn: "Bravo"))
    Given(mock, .surname(for name: .any, willReturn: "Kowalsky"))
    print(mock.surname(for: "Johny")) // Bravo
    print(mock.surname(for: "Mathew")) // Kowalsky
    print(mock.surname(for: "Joanna")) // Kowalsky

    View full-size slide

  12. SwiftyMocky: Given
    Given(mock, .surname(for name: .any, willReturn: "Bravo", "Kowalsky", "Nguyen"))
    print(mock.surname(for: "Johny")) // Bravo
    print(mock.surname(for: "Johny")) // Kowalsky
    print(mock.surname(for: "Johny")) // Nguyen
    print(mock.surname(for: "Johny")) // and again Bravo
    // ...

    View full-size slide

  13. SwiftyMocky: Verify
    sut.usersStorage = mockStorage
    sut.saveUser(name: "Johny", surname: "Bravo")
    sut.saveUser(name: "Johny", surname: "Cage")
    sut.saveUser(name: "Jon", surname: "Snow")
    Verify(mockStorage, .storeUser(name: .value("Jon"), surname: .value("Snow")))
    Verify(mockStorage, 3, .storeUser(name: .any, surname: .any))
    Verify(mockStorage, 2, .storeUser(name: .value("Johny"), surname: .any))
    Verify(mockStorage,
    .moreOrEqual(to: 2),
    .storeUser(name: .matching({ $0.count > 3 }}), surname: .any))
    Verify(mockStorage, .never, .deleteUser(id: .any))

    View full-size slide

  14. SwiftyMocky: Perform
    Perform(
    mock,
    .methodThatTakesCompletionBlock(
    completion: .any,
    perform: { completion in
    completion(true,nil)
    }
    )
    )

    View full-size slide

  15. Cuckoo
    • https://github.com/Brightify/Cuckoo
    • ಠࣗͷίʔυδΣωϨʔλʔ
    • SourceryϕʔεͰ͸ͳ͍͕ɺSourceKittenͱStencilΛ࢖ͬ
    ͍ͯΔͷ͸ಉ͡

    View full-size slide

  16. Cuckoo
    • ϓϩτίϧͷϞοΫ

    • ϓϩτίϧܧঝ

    • ΫϥεͷϞοΫ

    • δΣωϦΫε

    • 1.0.0͔Β
    • staticϝϯόʔ͸ඇαϙʔτ

    • Objective-CαϙʔτʢOCMockʹΑΔʣ

    • 1.2.0͔Β
    • ͦͷଞͷػೳ͸SwiftyMockyͱḮ৭ͳ͞
    ͦ͏
    • ߏจ͸ΑΓSwifty
    • ιʔεͷࢦఆ͸ϑΝΠϧ୯ҐͷΈ
    • importͷࣗಈղܾ

    View full-size slide

  17. Cuckoo: Stubbing
    then(_ implementation: IN throws -> OUT)
    thenReturn(_ output: OUT, _ outputs: OUT...)
    thenThrow(_ error: ErrorType, _ errors: Error...)
    thenCallRealImplementation()
    thenDoNothing()

    View full-size slide

  18. Cuckoo: Stubbing
    let mock = MockGreeter()
    // Methods
    stub(mock) { stub in
    when(stub.greetWithMessage("Hello world")).then { message in
    print(message)
    }
    }
    // Properties
    stub(mock) { stub in
    when(stub.readWriteProperty.get).thenReturn(10)
    when(stub.readWriteProperty.set(anyInt())).then {
    print($0)
    }
    }
    // Chain stubbing
    when(stub.readWriteProperty.get).thenReturn(10).thenReturn(20)
    when(stub.readWriteProperty.get).thenReturn(10, 20)

    View full-size slide

  19. Cuckoo: Verification
    // Parameter
    verify(mock).greetWithMessage("Hello world".or("Hallo Welt"))
    // Call
    verify(mock, times(3))...
    verify(mock, never())...
    verify(mock, atLeastOnce())...
    verify(mock, atLeast(2))...
    verify(mock, atMost(4))...

    View full-size slide

  20. Cuckoo: Argument capture
    mock.readWriteProperty = 10
    mock.readWriteProperty = 20
    mock.readWriteProperty = 30
    let argumentCaptor = ArgumentCaptor()
    verify(mock, times(3)).readWriteProperty.set(argumentCaptor.capture())
    argumentCaptor.value // Returns 30
    argumentCaptor.allValues // Returns [10, 20, 30]

    View full-size slide

  21. Mockolo
    • https://github.com/uber/mockolo
    • Uber੡
    • ίʔυੜ੒͕ߴ଎Ͱ͋Δ͜ͱ͕ചΓ
    • ಠࣗͷίʔυδΣωϨʔλʔ
    • ιʔείʔυͷύʔαʔΛSwiftSyntaxͱSourceKitten͔Βબ΂Δ
    • 2019೥12݄ϦϦʔεͷ1.1.0͔ΒSwiftSyntax͕࢖͑ΔΑ͏ʹʢσϑΥϧτ΋
    SwiftSyntaxʣ
    • /// @mockableͱ͍͏ΞϊςʔγϣϯίϝϯτͰର৅ΛࢦఆʢΦϓγϣϯͰมߋՄೳʣ

    View full-size slide

  22. Mockolo
    • ϓϩτίϧͷϞοΫ

    • ϓϩτίϧܧঝ

    • ΫϥεͷϞοΫ

    • δΣωϦΫε

    • staticϝϯόʔ

    • Objective-Cαϙʔτ

    • importͷࣗಈղܾ
    • ϑΝΠϧ୯Ґ͔σΟϨΫτ
    Ϧ୯ҐͷͲͪΒ͔ʢഉଞ
    తʣͰιʔεΛࢦఆ
    • verify/matcherͷΑ͏ͳ࢓
    ૊Έ͸ͳ͍
    • ૉ๿ʹXCTAssert͢Δ

    View full-size slide

  23. Mockolo:
    Performance
    • UIKonf 2019 - Day 1 - Ellie Shin -
    Mockolo: Efficient Mock
    Generator for Swift
    • https://www.youtube.com/watch?
    v=Tkg8721fObU

    View full-size slide

  24. Mockolo: Performance
    • SwiftyMockyΛಋೖࡁΈͷखݩͷϓϩδΣΫτͰൺֱ
    • SwiftyMocky:
    • Ωϟογϡͳ͠: 15.3ඵ
    • Ωϟογϡ͋Γ: 3ඵ
    • Mockolo: 0.5ඵ

    !

    View full-size slide

  25. Mockolo
    Input
    /// @mockable
    public protocol Foo {
    var num: Int { get set }
    func bar(arg: Float) -> String
    }

    View full-size slide

  26. Mockolo
    Output
    public class FooMock: Foo {
    init() {}
    init(num: Int = 0) {
    self.num = num
    }
    var numSetCallCount = 0
    var underlyingNum: Int = 0
    var num: Int {
    get {
    return underlyingNum
    }
    set {
    underlyingNum = newValue
    numSetCallCount += 1
    }
    }
    ...
    }
    public class FooMock: Foo {
    ...
    var barCallCount = 0
    var barHandler: ((Float) -> (String))?
    func bar(arg: Float) -> String {
    barCallCount += 1
    if let barHandler = barHandler {
    return barHandler(arg)
    }
    return ""
    }
    }

    View full-size slide

  27. Mockolo
    Usage
    func testMock() {
    let mock = FooMock(num: 5)
    XCTAssertEqual(mock.numSetCallCount, 1)
    mock.barHandler = { arg in
    return String(arg)
    }
    XCTAssertEqual(mock.barCallCount, 1)
    }

    View full-size slide

  28. Mockolo
    Protocol with associatedtype
    /// @mockable(typealias: T = AnyObject; U = StringProtocol)
    public protocol Foo {
    associatedtype T
    associatedtype U: Collection where U.Element == T
    associatedtype W
    ...
    }
    public class FooMock: Foo {
    typealias T = AnyObject // overriden
    typealias U = StringProtocol // overriden
    typealias W = Any // default placeholder type for typealias
    ...
    }

    View full-size slide

  29. ཁ݅ʹ߹ͬͨɾνʔϜʹదͨ͠બ୒ࢶΛબͼ·͠ΐ͏

    View full-size slide

  30. !
    Happy Swift Mocking/Testing

    View full-size slide

  31. WE ARE HIRING
    https://hatenacorp.jp/recruit/

    View full-size slide

  32. Thank you
    Sho Ikeda / @ikesyo

    View full-size slide