[cocoaheads tricity] andrzej dąbrowski - dependency injection and testable architecture in swift...

54
Testable apps in Swift Andrzej Dąbrowski [email protected] amplituda.io

Upload: cocoaheads-tricity

Post on 27-Jan-2017

176 views

Category:

Software


0 download

TRANSCRIPT

Page 1: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Testable apps in Swift

Andrzej Dąbrowski [email protected] amplituda.io

Page 2: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

A client comes…

Page 3: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

– Our Beloved Client

“I have a start-up, I need an app, previous team sucked so I’d like you to continue develop the

project”

Page 4: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Great!Let’s look at the code.

Page 5: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

1Split code to independent single-purpose services.

(no tests without that, sorry)

Page 6: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

2Write couple of tests.

Page 7: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Example service:

BeaconManager

Page 8: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Public API

Page 9: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Why should I make a wrapper around

CLLocationManager?

Page 10: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

• seamlessly manages permissions

• smoothens out beacon power fluctuations

• doesn’t require all listeners to duplicate single beacon filtering code and leaving beacon logic

• enable / disable listening

• saves energy (takes care of switching between monitoring/ranging automatically)

• takes care of background ranging (☠☠☠)

• it’s listeners actually don’t have to care about anything, only implement code on beacon events.

Page 11: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

BeaconManager uses another services/APIs

• CLLocationManager for permissions and background ranging (updating location)

• Logger (sends critical logs to back-end)

• Data

• SyncManager

• NSNotificationCenter

Page 12: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps
Page 13: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

BeaconManager is critical service to test

Page 14: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Singletons.Hard to test.

Page 15: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Objective-C dynamic runtime allows us easily change implementation of classes /

methods.

OCMock!

Page 16: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Can I do this in Swift?

Hmm…

Page 17: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Let’s readhttp://ocmock.org/swift/

Page 18: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

There are smart people around who claim differently:

http://nshipster.com/swift-objc-runtime/

IMVHO, not the best option.

Page 19: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Where is Swift going?

Will there ever be runtime access in Swift?

Page 20: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

I have no idea.

Page 21: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

But if Swift is about to be super-fast and super-safe,

I wouldn’t count on it.

Page 22: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Why Obj-C runtime is so great in testing?

• Effortless and quick mocks/stubs

• No refactoring needed at all. Just jump into writing tests.

Page 23: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Disadvantages of OCMock way of testing?

• No compile-time protection

• We can forget to stub some important things

• Encourages a bit to write ugly code (inline mocks / stubs) which could create problems with duplication later

• No explicit list of dependencies like in dependency injection

• Still have to write mocks / stubs :(

Page 24: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Alternatives?

Page 25: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Stubbing based on abstraction layer.

Service Locator or DI

Page 26: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

How does it work?

Page 27: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Service Locator

Page 28: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps
Page 29: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps
Page 30: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps
Page 31: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Advantages of SL

• compile-time

• quite quick to implement (actually, sharedInstance properties could be overwritten so that they call Service Locator themselves).

• Service protocols are a beautiful way to investigate app architecture without going too deep into implementation details

Page 32: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Disadvantages of SL

• We need to complicate architecture (is that really bad thing?)

• We can forget to stub some libs

• No explicit list of dependencies (like in DI)

• Still have to write our stubs :(

Page 33: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Dependency injection

"Dependency Injection" is a 25-dollar term for a 5-cent concept.

James Shore

Page 34: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

"Dependency Injection" is a 25-dollar term for a 5-cent concept.

James Shore

Page 35: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps
Page 36: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps
Page 37: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps
Page 38: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

We must initialise services and inject properties when

app starts.

Page 39: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

We could use some automation for initialising

services.

Page 40: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Advantages of DI• compile-time

• explicit list of dependencies (nice app architecture guard)

• we won’t forget to stub anything

• service protocols are a beautiful way to investigate app architecture without going too deep into implementation details

Page 41: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Disadvantages of DI

• Quite not-so-fast to implement

• Architecture gets more complicated (like in SL)

• We still need to write stubs

Page 42: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

SL vs. DI

• http://www.martinfowler.com/articles/injection.html

• http://bayou.io/draft/In_Defense_of_Service_Locator.html

• rest of the Internet

Page 43: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Are there any libraries for Dependency Injection?

Page 44: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Typhoon

Swinject

Page 45: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Typhoon

December, 2012 1667StarsFirst github commit

Page 46: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Swinject

July, 2015 454StarsFirst github commit

Page 47: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

There’s a single disadvantage of all testing

methods.

Page 48: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

No lib with stubs.

Page 49: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

I’d like to test like this

Page 50: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Why do I have to write mocks/stubs for Apple or

3rd party libs?

Page 51: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Let’s take a look at modern framework like

angular.js

Page 52: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Which iOS frameworks could be easily stubbed not to duplicate code?

Page 53: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Summary• method / class swizzling is probably not what you want

in Swift

• service locator is quickest way to get legacy code tested

• typhoon and swinject are 3rd party libs for Swift testing

• swinject seems to be nice

• unfortunately, there are no libraries which out of the box give developers stubs / mocks for Apple APIs.

Page 54: [CocoaHeads Tricity] Andrzej Dąbrowski - Dependency injection and testable architecture in Swift apps

Thank you