we are hiring! · testing your swift application with cuckoo matchers 21 • bool • string •...

27
Technology has enabled the jukebox to be so much more ~6 Million Users ~42 Million Check-Ins ~240 Million plays ~1.4K Ratings 4.7 stars in the app store Mobile App We are hiring! 1

Upload: others

Post on 21-May-2020

7 views

Category:

Documents


0 download

TRANSCRIPT

Technology has enabled the

jukebox to be so much more

• ~6 Million Users • ~42 Million Check-Ins • ~240 Million plays • ~1.4K Ratings • 4.7 stars in the app store

Mobile App

We are hiring!

�1

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

COCOAHEADS - OCTOBER 2018

Juan Garcia

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

WHY TESTING?▸ Bug detection

▸ Improve debugging process

▸ Quality of code

▸ Reduce cost

▸ Refactoring

�3

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

EXCUSES▸ Not enough time

▸ Technical Debt

▸ Lack of Knowledge

▸ Lack of Value

▸ Absence of Continuous Integration

�4

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

BASICS OF UNIT TESTING�5

TESTABLE UNIT

input 0 … …

input N

Expected result

Failure}

▸ Fast

▸ No external dependencies (ie. remote configuration)

▸ No communication with APIs (Integration Tests)

▸ Easy to write and understand

▸ Reliability (the test should always succeed)

*Design Flaw *Bug *Dependency change

Given When Then

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

SAMPLE APP�6

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

DESIGNING AND ARCHITECTING FOR TESTING�7

PRESENTER VIEW

API BROKER

Module

Event Handler User Interface

Broker

IMAGE EDITOR

ImageUtils

TESTING YOUR SWIFT APPLICATION WITH CUCKOO �8

PRESENTER VIEW

API BROKER

Module

Event Handler User Interface

Broker

Testable

IMAGE EDITOR

DESIGNING AND ARCHITECTING FOR TESTING

ImageUtils

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

MOCKING AND STUBBING�9

PRESENTER VIEW

API BROKER

Module

Event Handler User Interface

Broker

Mocks and Stubs

Mocks and Stubs

IMAGE EDITOR

ImageUtils

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

MOCKING AND STUBBING�10

Source: medium.com

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

DESIGNING AND ARCHITECTING FOR TESTING�11

protocol UserInterface: class { var eventHandler: EventHandler? { get set } func update(image: UIImage?) func setActive(filter: FilterType) func resetFilters()}

protocol EventHandler: class { func activate(filter: FilterType) func changeImage() func reset() func save()}

protocol Broker { func fetchImage(completion: (UIImage?) -> Void) func save(image: UIImage, success: (() -> Void)?, failure: ((Error) -> Void)?)}

Testable

protocol ImageUtils { func apply(filter: FilterType, input: CGImage, completion: @escaping (UIImage?) -> Void)}

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

IMPLEMENTATION�12

class Presenter { var userInterface: UserInterface? var broker: Broker? var imageEditor: ImageEditor?}

extension Presenter: EventHandler { func activate(filter: FilterType) {…} func changeImage() {…} func reset() {…} func save() {…}}

} Dependencies

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

WHAT IS CUCKOO?▸ Created due to lack of a proper Swift mocking

framework

▸ Similar to Mockito

▸ Due to a lack of proper reflection in Swift, Cuckoo use a compile-time code generation

▸ Makes it easier to create Mocks and Stubs

▸ Repo: https://github.com/Brightify/Cuckoo

�13

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

INSTALLING CUCKOO

carthage update --platform iOS

github "SwiftKit/Cuckoo"

▸ Add framework to Cartfile

▸ Run Carthage

▸ Add Framework to the Test target on Build Phases

�14

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

USING CUCKOO▸ Add Run Script phase to generate mocks

# Define output file.OUTPUT_FILE="$PROJECT_DIR/${PROJECT_NAME}Tests/GeneratedMocks.swift"echo "Generated Mocks File = $OUTPUT_FILE"

# Define input directory.INPUT_DIR="${PROJECT_DIR}/${PROJECT_NAME}"echo "Mocks Input Directory = $INPUT_DIR"

# Generate mock files, include as many input files as you'd like."Carthage/Checkouts/Cuckoo/run" generate --testable "$PROJECT_NAME" \--output "${OUTPUT_FILE}" \"$INPUT_DIR/FileName1.swift" \"$INPUT_DIR/FileName2.swift" \"$INPUT_DIR/FileName3.swift"# ... and so forth, the last line should never end with a backslash

# After running once, locate `GeneratedMocks.swift` and drag it into your Xcode test target group.

�15

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

USING CUCKOO�16

PRESENTER

Module

Event Handler User Interface

Broker

Testable

VIEW

API BROKER IMAGE EDITOR

ImageUtils

VIEW

API BROKER IMAGE EDITOR

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

USING CUCKOO�17

PRESENTER MOCK VIEW

MOCK API BROKER

Module

Event Handler User Interface

Broker

Testable

MOCK IMAGE EDITOR

ImageUtils

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

USING CUCKOO

let presenter = Presenter() // Class to test

// Dependenciespresenter.userInterface = MockUserInterface()presenter.broker = MockBroker()presenter.imageEditor = MockImageEditor()

▸ Initialization

▸ Spy

// Spies only work with mocked classes not protocols let spy = MockClass().withEnabledSuperclassSpy()

�18

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

TEST #1 - RESET FILTERS�19

▸ Tap on the Reset Button

▸ The filter swift turn off

▸ The image is updated to the original

TESTING YOUR SWIFT APPLICATION WITH CUCKOO �20

// Givenlet presenter = Presenter()presenter.userInterface = MockUserInterface()

func testResetFilter() {

guard let mockUserInterface = presenter.userInterface as? MockUserInterface else { XCTFail(); return }

var filtersWereReset = falsevar userInterfaceWasUpdated = false

// Whenstub(mockUserInterface) { stub in when(stub.resetFilters()).then { filtersWereReset = true }}stub(mockUserInterface) { stub in when(stub.update(image: any())).then { _ in userInterfaceWasUpdated = true }}

presenter.reset()// ThenXCTAssertTrue(filtersWereReset && userInterfaceWasUpdated)

}

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

MATCHERS�21

• Bool• String• Float• Double• Character• Int• Int8•

You can mock any object that conforms to the Matchable protocol. These basic values are extended to conform to Matchable:

or…public func any<T>(_ type: T.Type = default) -> Cuckoo.ParameterMatcher<T>

public func anyClosure<IN, OUT>() -> Cuckoo.ParameterMatcher<(IN) -> OUT>

public func anyInt() -> Cuckoo.ParameterMatcher<Int?>

public func anyOptionalThrowingClosure<IN, OUT>() -> Cuckoo.ParameterMatcher<(((IN)) throws -> OUT)?>

public func anyString() -> Cuckoo.ParameterMatcher<String>

• Int16• Int32• Int64• UInt• UInt8• UInt16• UInt32• UInt64

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

TEST #2- APPLY FILTERS�22

▸ Activate on the filters switch to apply it

▸ The filter is applied over the original image

▸ The image is updated with the filtered version

TESTING YOUR SWIFT APPLICATION WITH CUCKOO �23

func testSepiaFilterApplication() {

}

guard let mockEditor = presenter.imageEditor as? MockImageEditor, let mockUserInterface = presenter.userInterface as? MockUserInterface else { XCTFail(); return }

// Givenlet presenter = Presenter() presenter.userInterface = MockUserInterface()presenter.imageEditor = MockImageEditor()

var filterWasApplied = falsevar properFilterSwitchWasActivated = falsevar userInterfaceWasUpdated = false

let testImage = UIImage(named: "city1")

presenter.originalImage = testImage

. . .

TESTING YOUR SWIFT APPLICATION WITH CUCKOO �24

func testSepiaFilterApplication() {

}

. . . // Whenstub(mockUserInterface) { stub in stub.setActive(filter: any()).then { filter in properFilterSwitchWasActivated = (filter == .sepia) }}

stub(mockEditor) { stub in stub.apply(filter: any(), input: any(), completion: any()).then { filter, inputImage, completion in filterWasApplied = (filter == .sepia) && (inputImage == testImage?.cgImage) completion(testImage) verify(mockUserInterface).update(image: any()) }}

stub(mockUserInterface) { stub in stub.update(image: any()).then { imageToUpdate in userInterfaceWasUpdatedWithTestImage = (imageToUpdate == testImage) }}

presenter.activate(filter: .sepia)

// ThenXCTAssertTrue(filterWasApplied && userInterfaceWasUpdated && properFilterSwitchWasActivated)

verify(mockUserInterface).setActive(filter: any())

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

SAMPLE APP�25

https://github.com/jotajota05/cuckoo-example

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

CONCLUSION�26

▸ Design and architecture are key when adding Unit Tests

▸ Separation of responsibility

▸ Small testable units

▸ Easier Mocking and Stubbing in Swift with Cuckoo framework

TESTING YOUR SWIFT APPLICATION WITH CUCKOO

THANK YOU!QUESTIONS?

�27