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

2019年のSwiftモック事情

7ddcca09c00a2744b983974225447d19?s=47 Sho Ikeda
December 16, 2019

 2019年のSwiftモック事情

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

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

7ddcca09c00a2744b983974225447d19?s=128

Sho Ikeda

December 16, 2019
Tweet

Transcript

  1. 2019೥ͷ SwiftϞοΫࣄ৘ Sho Ikeda / @ikesyo ೥຤ͩΑ Android/iOS Test Night

    - 2019 2019-12-16 Mon #test_night
  2. Sho Ikeda / @ikesyo • ͍͚͠ΐʔʗ஑ా ᠳ • ͸ͯͳ@ژ౎ •

    εϚʔτϑΥϯΞϓϦ։ൃ • iOS / Android / React Native • Swiftίϛολʔ • swift-corelibs-foundation • Quick/Nimbleͷϝϯςφʔ • https://twitter.com/ikesyo • https://github.com/ikesyo
  3. ͋ΘͤͯಡΈ͍ͨ • Swiftʹ͓͚ΔMockϥΠϒϥϦͷ׆ ༻/swift-mock-library • https://speakerdeck.com/ yusukehosonuma/swift-mock- library

  4. How to Mock Protocols in Swift • Manually ! •

    Sourcery: AutoMockable • SwiftyMocky • Cuckoo • Mockolo <- "❗
  5. Sourcery: AutoMockable

  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
  7. Sourcery: AutoMockable • ϞοΫ༻ͷςϯϓϨʔτ • Mocks.md • AutoMockable.stencil • ର৅ͱͳΔͷ͸

    • AutoMockable ͱ͍͏໊લͷϓϩτίϧʹ४ڌ • // sourcery: AutoMockable ͷΞϊςʔγϣϯίϝϯτͷ෇ ༩
  8. Sourcery: AutoMockable • ϝιου • ݺ͹Ε͔ͨͲ͏͔ͷνΣοΫ: ճ਺͸෼͔Βͳ͍ • Ҿ਺λϓϧͷϓϩύςΟ: ݺ͹ΕͨҾ਺ͷνΣοΫ

    • ໭Γ஋ઃఆ༻ͷϓϩύςΟ • ϓϩύςΟఆٛ • ͔ͳΓγϯϓϧ • ֎෦Ҿ਺໊͕ಉҰɾܕҧ͍ͷΦʔόʔϩʔυ͸NG • ϓϩτίϧఆٛதʹ֎෦ϑϨʔϜϫʔΫͷܕ͕ొ৔͢Δ৔߹ɺࣗ෼ͰςϯϓϨʔτΛมߋ ͯ͠importΛॻ͖Ճ͑Δඞཁ͕͋Δ
  9. SwiftyMocky

  10. SwiftyMocky • https://github.com/MakeAWishFoundation/SwiftyMocky • Sourceryϕʔε • AutoMockableͷϚʔΫΛͦͷ··࢖༻ • ΑΓߴػೳͳςϯϓϨʔτͱϥϯλΠϜ •

    SourceryͷAutoMockableςϯϓϨʔτ͔ΒҠߦ͠΍͍͢ • Mockfileͱ͍͏ઃఆϑΝΠϧͱɺswiftymockyͱ͍͏ϥούʔί Ϛϯυ͕༻ҙ͞Ε͍ͯΔ
  11. SwiftyMocky • ϓϩτίϧͷϞοΫ ✅ • ϓϩτίϧܧঝ ✅ • ΫϥεͷϞοΫ ❌

    • δΣωϦΫε ✅ • Handling Generics.md • staticϝϯόʔ ✅ • Objective-Cαϙʔτʢ@objcϓϩτίϧʣ ✅ • ϑΝΠϧɾσΟϨΫτϦ୯ҐͰιʔεΛࢦ ఆ • ઃఆϑΝΠϧͰimport͢ΔϑϨʔϜϫʔΫ ΛࢦఆՄೳ • swiftymocky autoimportίϚϯυͰࣗ ಈੜ੒Մೳ • GivenʹΑΔελϒʢ໭Γ஋ͷࢦఆʣ • VerifyʹΑΔϝιουݺͼग़͠ͷݕࠪ • ճ਺΍Ҿ਺ • PerformʹΑΔ೚ҙͷΫϩʔδϟͷ࣮ߦ
  12. 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
  13. 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 // ...
  14. 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))
  15. SwiftyMocky: Perform Perform( mock, .methodThatTakesCompletionBlock( completion: .any, perform: { completion

    in completion(true,nil) } ) )
  16. Cuckoo

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

  18. Cuckoo • ϓϩτίϧͷϞοΫ ✅ • ϓϩτίϧܧঝ ✅ • ΫϥεͷϞοΫ ✅

    • δΣωϦΫε ✅ • 1.0.0͔Β • staticϝϯόʔ͸ඇαϙʔτ ❌ • Objective-CαϙʔτʢOCMockʹΑΔʣ ✅ • 1.2.0͔Β • ͦͷଞͷػೳ͸SwiftyMockyͱḮ৭ͳ͞ ͦ͏ • ߏจ͸ΑΓSwifty • ιʔεͷࢦఆ͸ϑΝΠϧ୯ҐͷΈ • importͷࣗಈղܾ
  19. Cuckoo: Stubbing then(_ implementation: IN throws -> OUT) thenReturn(_ output:

    OUT, _ outputs: OUT...) thenThrow(_ error: ErrorType, _ errors: Error...) thenCallRealImplementation() thenDoNothing()
  20. 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)
  21. 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))...
  22. Cuckoo: Argument capture mock.readWriteProperty = 10 mock.readWriteProperty = 20 mock.readWriteProperty

    = 30 let argumentCaptor = ArgumentCaptor<Int>() verify(mock, times(3)).readWriteProperty.set(argumentCaptor.capture()) argumentCaptor.value // Returns 30 argumentCaptor.allValues // Returns [10, 20, 30]
  23. Mockolo

  24. Mockolo • https://github.com/uber/mockolo • Uber੡ • ίʔυੜ੒͕ߴ଎Ͱ͋Δ͜ͱ͕ചΓ • ಠࣗͷίʔυδΣωϨʔλʔ •

    ιʔείʔυͷύʔαʔΛSwiftSyntaxͱSourceKitten͔Βબ΂Δ • 2019೥12݄ϦϦʔεͷ1.1.0͔ΒSwiftSyntax͕࢖͑ΔΑ͏ʹʢσϑΥϧτ΋ SwiftSyntaxʣ • /// @mockableͱ͍͏ΞϊςʔγϣϯίϝϯτͰର৅ΛࢦఆʢΦϓγϣϯͰมߋՄೳʣ
  25. Mockolo • ϓϩτίϧͷϞοΫ ✅ • ϓϩτίϧܧঝ ✅ • ΫϥεͷϞοΫ ❌

    • δΣωϦΫε ✅ • staticϝϯόʔ ✅ • Objective-Cαϙʔτ ❌ • importͷࣗಈղܾ • ϑΝΠϧ୯Ґ͔σΟϨΫτ Ϧ୯ҐͷͲͪΒ͔ʢഉଞ తʣͰιʔεΛࢦఆ • verify/matcherͷΑ͏ͳ࢓ ૊Έ͸ͳ͍ • ૉ๿ʹXCTAssert͢Δ
  26. Mockolo: Performance • UIKonf 2019 - Day 1 - Ellie

    Shin - Mockolo: Efficient Mock Generator for Swift • https://www.youtube.com/watch? v=Tkg8721fObU
  27. Mockolo: Performance • SwiftyMockyΛಋೖࡁΈͷखݩͷϓϩδΣΫτͰൺֱ • SwiftyMocky: • Ωϟογϡͳ͠: 15.3ඵ •

    Ωϟογϡ͋Γ: 3ඵ • Mockolo: 0.5ඵ • !
  28. Mockolo Input /// @mockable public protocol Foo { var num:

    Int { get set } func bar(arg: Float) -> String }
  29. 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 "" } }
  30. Mockolo Usage func testMock() { let mock = FooMock(num: 5)

    XCTAssertEqual(mock.numSetCallCount, 1) mock.barHandler = { arg in return String(arg) } XCTAssertEqual(mock.barCallCount, 1) }
  31. 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 ... }
  32. ཁ݅ʹ߹ͬͨɾνʔϜʹదͨ͠બ୒ࢶΛબͼ·͠ΐ͏

  33. ! Happy Swift Mocking/Testing

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

  35. Thank you Sho Ikeda / @ikesyo