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. Sho Ikeda / @ikesyo • ͍͚͠ΐʔʗ஑ా ᠳ • ͸ͯͳ@ژ౎ •

    εϚʔτϑΥϯΞϓϦ։ൃ • iOS / Android / React Native • Swiftίϛολʔ • swift-corelibs-foundation • Quick/Nimbleͷϝϯςφʔ • https://twitter.com/ikesyo • https://github.com/ikesyo
  2. How to Mock Protocols in Swift • Manually ! •

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

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

    • ໭Γ஋ઃఆ༻ͷϓϩύςΟ • ϓϩύςΟఆٛ • ͔ͳΓγϯϓϧ • ֎෦Ҿ਺໊͕ಉҰɾܕҧ͍ͷΦʔόʔϩʔυ͸NG • ϓϩτίϧఆٛதʹ֎෦ϑϨʔϜϫʔΫͷܕ͕ొ৔͢Δ৔߹ɺࣗ෼ͰςϯϓϨʔτΛมߋ ͯ͠importΛॻ͖Ճ͑Δඞཁ͕͋Δ
  6. SwiftyMocky • https://github.com/MakeAWishFoundation/SwiftyMocky • Sourceryϕʔε • AutoMockableͷϚʔΫΛͦͷ··࢖༻ • ΑΓߴػೳͳςϯϓϨʔτͱϥϯλΠϜ •

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

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

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

    OUT, _ outputs: OUT...) thenThrow(_ error: ErrorType, _ errors: Error...) thenCallRealImplementation() thenDoNothing()
  13. 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)
  14. 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))...
  15. 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]
  16. Mockolo • https://github.com/uber/mockolo • Uber੡ • ίʔυੜ੒͕ߴ଎Ͱ͋Δ͜ͱ͕ചΓ • ಠࣗͷίʔυδΣωϨʔλʔ •

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

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

    Shin - Mockolo: Efficient Mock Generator for Swift • https://www.youtube.com/watch? v=Tkg8721fObU
  19. Mockolo Input /// @mockable public protocol Foo { var num:

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

    XCTAssertEqual(mock.numSetCallCount, 1) mock.barHandler = { arg in return String(arg) } XCTAssertEqual(mock.barCallCount, 1) }
  22. 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 ... }