Slide 1

Slide 1 text

Good morning!

Slide 2

Slide 2 text

Real World Mocking In Swift

Slide 3

Slide 3 text

You Want To Write Tests

Slide 4

Slide 4 text

But You Don’t Want Them To Mess Up Your Real Stuff

Slide 5

Slide 5 text

Or Be Slow

Slide 6

Slide 6 text

let session = NSURLSession() let url = NSURL(string: "http://www.tryswiftconf.com")! let task = session.dataTaskWithURL(url) { (data, _, _) -> Void in if let data = data { let string = String(data: data, encoding: NSUTF8StringEncoding) print(string) } } task.resume()

Slide 7

Slide 7 text

OCMock can't save you now

Slide 8

Slide 8 text

class HTTPClientTests: XCTestCase { var subject: HTTPClient! let session = MockURLSession() override func setUp() { super.setUp() subject = HTTPClient(session: session) }

Slide 9

Slide 9 text

class HTTPClientTests: XCTestCase { var subject: HTTPClient! let session = MockURLSession() override func setUp() { super.setUp() subject = HTTPClient(session: session) } func test_GET_RequestsTheURL() { let url = NSURL(string: "http://www.tryswiftconf.com")! subject.get(url) { (_, _) -> Void in }

Slide 10

Slide 10 text

class HTTPClientTests: XCTestCase { var subject: HTTPClient! let session = MockURLSession() override func setUp() { super.setUp() subject = HTTPClient(session: session) } func test_GET_RequestsTheURL() { let url = NSURL(string: "http://www.tryswiftconf.com")! subject.get(url) { (_, _) -> Void in } XCTAssert(session.lastURL === url) } }

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

It Will Take A Lot Of Time

Slide 13

Slide 13 text

You Will Wonder If It’s Worth It

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Why Use Mocks » Make tests faster (like 1000s of times faster!) ! » Increase coverage of test suite " » Make tests more robust #

Slide 16

Slide 16 text

Testing

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

The Time To Write Tests Is

Slide 19

Slide 19 text

Now

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Dependency Injection

Slide 23

Slide 23 text

class VideoCaptureManager: NSObject { var userDefaults: UserDefaultsProtocol //MARK: - Initializers override init() { self.userDefaults = UserDefaults() } convenience init(userDefaults: UserDefaultsProtocol) { self.init() self.userDefaults = userDefaults } }

Slide 24

Slide 24 text

“Dependency injection means giving an object its instance variables. Really. That's it.” James Shore

Slide 25

Slide 25 text

Why Not Just Use A Singleton?

Slide 26

Slide 26 text

class VideoUploadManager { static let sharedInstance = VideoUploadManager() } class TimeMachineAPI { func uploadVideo(videoRecording: VideoRecording) { VideoUploadManager.sharedInstance.upload(videoRecording: self.videoRecording, completion: { (error) -> Void in if let error = error { Log.error(error.localizedDescription) } }) } }

Slide 27

Slide 27 text

Why Use Dependency Injection » Easy customization ! » Clear ownership " » Testability #

Slide 28

Slide 28 text

Constructor injection

Slide 29

Slide 29 text

Test Doubles

Slide 30

Slide 30 text

Types of Test Doubles » Stubs ! » Mocks " » Partial mocks #

Slide 31

Slide 31 text

Stubs

Slide 32

Slide 32 text

“Fakes a response to method calls of an object” Unit Testing Tutorial: Mocking Objects

Slide 33

Slide 33 text

class StubTimeMachineAPI: TimeMachineAPI { var videoUrl = "https://www.youtube.com/watch?v=SQ8aRKG9660" func getVideoFor(year: Int) -> String { return videoUrl } }

Slide 34

Slide 34 text

Mocks

Slide 35

Slide 35 text

“Let you check if a method call is performed or if a property is set” Unit Testing Tutorial: Mocking Objects

Slide 36

Slide 36 text

class MockTimeMachine: TimeMachine { var timeTravelWasCalled = false mutating func timeTravelTo(year: Int) { timeTravelWasCalled = true } }

Slide 37

Slide 37 text

Partial Mock

Slide 38

Slide 38 text

“Any actual object which has been wrapped or changed ” Justin Searls

Slide 39

Slide 39 text

“to provide artificial responses to some methods but not others” Justin Searls

Slide 40

Slide 40 text

Partial Mocks Are An Anti-pattern

Slide 41

Slide 41 text

What’s Wrong With Partial Mocks? » Challenging to set up ! » Decreases the comprehensibility of the test "

Slide 42

Slide 42 text

What’s Real? What’s Fake?

Slide 43

Slide 43 text

Mocking

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

Mocking In Swift Via Protocols

Slide 46

Slide 46 text

“This kind of testing is really similar to what you get with mocks, but it’s so much better.” Protocol-Oriented Programming in Swift

Slide 47

Slide 47 text

“Mocks are inherently fragile. ” Protocol-Oriented Programming in Swift

Slide 48

Slide 48 text

“You have to couple your testing code” Protocol-Oriented Programming in Swift

Slide 49

Slide 49 text

“to the implementation details of the code under test.” Protocol-Oriented Programming in Swift

Slide 50

Slide 50 text

Plays Well With Structs And Classes

Slide 51

Slide 51 text

Protocols Help When There’s Internal Dependencies

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

They Said Don’t Mock Types You Don’t Own

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

Why It’s Bad To Mock Types You Don’t Own » Have to be sure that the behavior you implement in a mock matches the external library » The external library could change, breaking your mock

Slide 56

Slide 56 text

Mocking Apple Framework Classes class UserDefaultsMock: UserDefaultsProtocol { private var objectData = [String : AnyObject]() func objectForKey(key: UserDefaultsKey) -> AnyObject? { return objectData[key.name] } func setObject(value: AnyObject?, forKey key: UserDefaultsKey) { objectData[key.name] = value } func removeObjectForKey(key: UserDefaultsKey) { objectData[key.name] = nil } }

Slide 57

Slide 57 text

Time Traveler Times

Slide 58

Slide 58 text

Test That Uses UserDefaultsMock func testNotificationSettingsLoad() { let userDefaultsMock = UserDefaultsMock() mockUserDefaults.setObject("NEVER", forKey: "timeMachineBreakingNewsPushNotification") mockUserDefaults.setObject("NEVER", forKey: "timeMachineDailyDigestPushNotification") let dataProvider = PushNotificationsDataProvider(userDefaults: mockUserDefaults) let expectedFrequencies: [PushNotificationFrequency] = [.Never, .All, .All, .Never] XCTAssertEqual(expectedFrequencies, dataProvider.notificationSettings.values) }

Slide 59

Slide 59 text

Mocking NSNotificationCenter Was Not Worth It » Complex ! » Injected throughout entire codebase " » Limited functionality compared to real object # » Solves a problem that doesn’t really exist $

Slide 60

Slide 60 text

They Said Don’t Mock Value Types

Slide 61

Slide 61 text

“When you’re doing...simple data in and data out, you don’t need mocking or stubbing. ” Andy Matuschak

Slide 62

Slide 62 text

“You can pass a value into a function, then look at the resulting value.” Andy Matuschak

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

“Try making your reference types immutable” Joe Groff

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

What Makes a Good Mock » Quick and easy to write » Relatively short and does not contain tons of information you don’t need » Legitimate reason to not use real object

Slide 67

Slide 67 text

I am thoughtful about what gets mocked and why

Slide 68

Slide 68 text

Fellow time travelers

Slide 69

Slide 69 text

Let’s look towards the future

Slide 70

Slide 70 text

Swift Mocking Frameworks

Slide 71

Slide 71 text

Dobby MockFive SwiftMock

Slide 72

Slide 72 text

Cuckoo

Slide 73

Slide 73 text

A World Without Mocks

Slide 74

Slide 74 text

Wrap Up ! If you want to improve your codebase, write tests ! Mocks allow you to write great tests ! Create simple mocks that will last

Slide 75

Slide 75 text

Write Mocks And Use Them For Great Good

Slide 76

Slide 76 text

! " !