Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Sourcery: AutoMockable

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

SwiftyMocky

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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 // ...

Slide 14

Slide 14 text

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))

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

Cuckoo

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Cuckoo • ϓϩτίϧͷϞοΫ ✅ • ϓϩτίϧܧঝ ✅ • ΫϥεͷϞοΫ ✅ • δΣωϦΫε ✅ • 1.0.0͔Β • staticϝϯόʔ͸ඇαϙʔτ ❌ • Objective-CαϙʔτʢOCMockʹΑΔʣ ✅ • 1.2.0͔Β • ͦͷଞͷػೳ͸SwiftyMockyͱḮ৭ͳ͞ ͦ͏ • ߏจ͸ΑΓSwifty • ιʔεͷࢦఆ͸ϑΝΠϧ୯ҐͷΈ • importͷࣗಈղܾ

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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)

Slide 21

Slide 21 text

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))...

Slide 22

Slide 22 text

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]

Slide 23

Slide 23 text

Mockolo

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Mockolo • ϓϩτίϧͷϞοΫ ✅ • ϓϩτίϧܧঝ ✅ • ΫϥεͷϞοΫ ❌ • δΣωϦΫε ✅ • staticϝϯόʔ ✅ • Objective-Cαϙʔτ ❌ • importͷࣗಈղܾ • ϑΝΠϧ୯Ґ͔σΟϨΫτ Ϧ୯ҐͷͲͪΒ͔ʢഉଞ తʣͰιʔεΛࢦఆ • verify/matcherͷΑ͏ͳ࢓ ૊Έ͸ͳ͍ • ૉ๿ʹXCTAssert͢Δ

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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 "" } }

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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 ... }

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

! Happy Swift Mocking/Testing

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Thank you Sho Ikeda / @ikesyo