Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Protocol-Oriented Testing in Swift
Search
Carsten Könemann
May 12, 2017
Programming
140
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Protocol-Oriented Testing in Swift
Carsten Könemann
May 12, 2017
Other Decks in Programming
See All in Programming
変化を抱擁するドキュメントの作り方 - ビジネスルール駆動開発がもたらす、コードとの新しい関係
ioki
2
150
Kiroで創り、AgentCoreで繋ぐ!AWSで実践する「AI-DLC」から「AIエージェント統合」までの最新地図
licux
4
640
Webの地図
yosuke_furukawa
PRO
6
4k
What We Talk About When We Talk About XP
m_seki
2
470
新卒PdEのリアル
ryu1013
1
480
The Good Stuff, Not the Slop: Engineering High-Quality Android Apps with Modern AI Tooling
danybony
1
240
型解析で実現する Go の言語内 DSL / Conference に Go! タイムテーブルの歩き方 for Gophers
mazrean
0
140
新人はどこまで自力でやり、どこからAIに頼るべきか/エンジニア育成に向き合う_先輩たちの悩みと知見共有会
toppan_digital_dev
1
600
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
140
20260828_品質と開発生産性を両立させる、AI時代のE2Eテストの考え方
magicpod
0
190
Seeing Through Serverless: Observability for AWS Lambda with ADOT and CloudWatch Application Signals
seike460
PRO
1
130
Claude Codeを組織的に動かして月400PRを実現した話
happy_ryo
0
270
Featured
See All Featured
Tell your own story through comics
letsgokoyo
1
1.1k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
370
[RailsConf 2023] Rails as a piece of cake
palkan
59
7k
Abbi's Birthday
coloredviolet
3
9.9k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
Speed Design
sergeychernyshev
33
2.1k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
840
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
440
GitHub's CSS Performance
jonrohan
1033
470k
A Tale of Four Properties
chriscoyier
163
24k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
570
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
880
Transcript
Protocol-Oriented Testing in Swift Carsten Könemann, Software Engineer @ hmmh
1
Hello, world! • Carsten Könemann • Software Engineer @ hmmh
• iOS, Android Carsten Könemann, Software Engineer @ hmmh 2
Protocol-Oriented • Swift is a "protocol-oriented" programming language1 • Swift
protocol == Java interface • but more powerful • Default implementations • Composable 1 Apple, WWDC 2015; https://developer.apple.com/videos/play/wwdc2015/408/. Carsten Könemann, Software Engineer @ hmmh 3
Testing Good tests are • Repeatable • Independent • Fast
Carsten Könemann, Software Engineer @ hmmh 4
Testing Good tests are • Easy to write and execute
• Input → Function to test → Check output • Avoid any logic Carsten Könemann, Software Engineer @ hmmh 5
Testing Carsten Könemann, Software Engineer @ hmmh 6
Mock Objects • Simulate input → deterministic • Decoupled from
other code, database, network, etc → independent • No need to run on device / start UI → fast Carsten Könemann, Software Engineer @ hmmh 7
Example 1 Simple Mock Carsten Könemann, Software Engineer @ hmmh
8
Example 1 Simple Mock protocol Pet { func feed() }
Carsten Könemann, Software Engineer @ hmmh 9
Example 1 Simple Mock class Cat: Pet { func feed()
{ print("miau") } } Carsten Könemann, Software Engineer @ hmmh 10
Example 1 Simple Mock class Fish: Pet { var mealsCount:
Int = 0 func die() { print("blub") } func feed() { mealsCount += 1 if mealsCount > 9 { die() } } } Carsten Könemann, Software Engineer @ hmmh 11
Example 1 Simple Mock class Owner { var pet: Pet
init(pet: Pet) { self.pet = pet } func beResponsible() { pet.feed() } } Carsten Könemann, Software Engineer @ hmmh 12
Example 1 Simple Mock class MockPet: Pet { var feedCallCount:
Int = 0 func feed() { feedCallCount += 1 } } Carsten Könemann, Software Engineer @ hmmh 13
Example 1 Simple Mock class OwnerTests: XCTestCase { func testBeResponsible()
{ let pet = MockPet() let owner = Owner(pet: pet) owner.beResponsible() XCTAssert(pet.feedCallCount > 0) XCTAssert(pet.feedCallCount < 10) } } Carsten Könemann, Software Engineer @ hmmh 14
Example 1 Simple Mock extension MockPet { func validateFeedCallCount() {
XCTAssert(feedCallCount > 0) XCTAssert(feedCallCount < 10) } } class OwnerTests: XCTestCase { func testBeResponsibleImproved() { let pet = MockPet() let owner = Owner(pet: pet) owner.beResponsible() pet.validateFeedCallCount() } } Carsten Könemann, Software Engineer @ hmmh 15
Example 2 CoreData Or: Why not just use subclasses? Carsten
Könemann, Software Engineer @ hmmh 16
Example 2 CoreData @objc(MyCoreDataModel) public class MyCoreDataModel: NSManagedObject { @nonobjc
public class func fetchRequest() -> NSFetchRequest<MyCoreDataModel> { return NSFetchRequest<MyCoreDataModel>(entityName: "MyCoreDataModel") } @NSManaged public var foobar: Bool } Carsten Könemann, Software Engineer @ hmmh 17
Example 2 CoreData class MyCoreDataController { static func doSomething(with model:
MyCoreDataModel) -> String { if model.foobar == true { return "yay" } else { return "nay" } } } Carsten Könemann, Software Engineer @ hmmh 18
Example 2 CoreData class MyCoreDataControllerTests: XCTestCase { func testExample01() {
let coreDataModel = MyCoreDataModel() coreDataModel.foobar = true XCTAssertEqual(MyCoreDataController.doSomething(with: coreDataModel), "yay") } } Carsten Könemann, Software Engineer @ hmmh 19
Example 2 CoreData class MyCoreDataControllerTests: XCTestCase { func testExample01() {
let coreDataModel = MyCoreDataModel() coreDataModel.foobar = true XCTAssertEqual(MyCoreDataController.doSomething(with: coreDataModel), "yay") // failed: caught "NSInvalidArgumentException" // -[MyCoreDataModel setFoobar:]: unrecognized selector sent to instance } } Carsten Könemann, Software Engineer @ hmmh 20
Example 2 CoreData class MyCoreDataControllerTests: XCTestCase { func testExample01() {
let coreDataModel = MyCoreDataModel() coreDataModel.foobar = true XCTAssertEqual(MyCoreDataController.doSomething(with: coreDataModel), "yay") // failed: caught "NSInvalidArgumentException" // -[MyCoreDataModel setFoobar:]: unrecognized selector sent to instance // CoreData Entities need be associated with an NSManagedObjectContext! } } Carsten Könemann, Software Engineer @ hmmh 21
Example 2 CoreData extension XCTestCase { func setUpInMemoryManagedObjectContext() -> NSManagedObjectContext
{ let managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle.main])! let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) do { try persistentStoreCoordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil) } catch { print("Adding in-memory persistent store failed") } let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator return managedObjectContext } } Source: http://stackoverflow.com/a/39317100/1028701 Carsten Könemann, Software Engineer @ hmmh 22
Example 2 CoreData class MyCoreDataControllerTests: XCTestCase { var managedObjectContext: NSManagedObjectContext?
override func setUp() { super.setUp() if managedObjectContext == nil { managedObjectContext = setUpInMemoryManagedObjectContext() } } func testExample02() { let coreDataModel = MyCoreDataModel(context: managedObjectContext!) coreDataModel.foobar = true XCTAssertEqual(MyCoreDataController.doSomething(with: coreDataModel), "yay") } } Carsten Könemann, Software Engineer @ hmmh 23
Example 2 CoreData Reminder: Good tests are • Deterministic •
Independent • Fast • Easy to write Carsten Könemann, Software Engineer @ hmmh 24
Example 2 CoreData protocol MyCoreDataModelProtocol { var foobar: Bool {
get } } extension MyCoreDataModel: MyCoreDataModelProtocol { // } Carsten Könemann, Software Engineer @ hmmh 25
Example 2 CoreData class MyCoreDataModelMock: MyCoreDataModelProtocol { var foobar: Bool
} class MyCoreDataControllerTests: XCTestCase { func testExample03() { let coreDataModel = MyCoreDataModelMock() coreDataModel.foobar = true XCTAssertEqual(MyCoreDataController.doSomething(with: coreDataModel), "yay") } } Carsten Könemann, Software Engineer @ hmmh 26
Example 2 CoreData class MyCoreDataModelMock: MyCoreDataModelProtocol { // Use a
stub for static test data var foobar: Bool { return true } } class MyCoreDataControllerTests: XCTestCase { func testExample03() { XCTAssertEqual(MyCoreDataController.doSomething(with: MyCoreDataModelMock()), "yay") } } Carsten Könemann, Software Engineer @ hmmh 27
Example 2 CoreData Seriously, why not just use subclasses? Carsten
Könemann, Software Engineer @ hmmh 28
Example 2 CoreData class GeneratedCoreDataModel { ... } protocol CoreDataModelProtocol
{ ... } class CoreDataModel: GeneratedCoreDataModel, CoreDataModelProtocol { ... } class CoreDataMock: CoreDataModelProtocol { ... } Carsten Könemann, Software Engineer @ hmmh 29
Example 3 CoreLocation Or: Mocking other peoples code Carsten Könemann,
Software Engineer @ hmmh 30
Example 3 CoreLocation class MyCoreLocationController: NSObject, CLLocationManagerDelegate { var locationManager:
CLLocationManager? { didSet { locationManager?.delegate = self locationManager?.startMonitoring(for: CLCircularRegion(center: CLLocationCoordinate2D(latitude: 0, longitude: 0), radius: 5, identifier: "foo") ) } } var didVisitRegion = false func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) { self.didVisitRegion = true } } Carsten Könemann, Software Engineer @ hmmh 31
Example 3 CoreLocation Traditionally tested: • Running around the office
• Simulate location with Xcode Carsten Könemann, Software Engineer @ hmmh 32
Example 3 CoreLocation protocol CLLocationManagerProtocol { var delegate: CLLocationManagerDelegate? {
get set } var monitoredRegions: Set<CLRegion> { get } func startMonitoring(for region: CLRegion) } extension CLLocationManager: CLLocationManagerProtocol { // } Carsten Könemann, Software Engineer @ hmmh 33
Example 3 CoreLocation class CLLocationManagerMock: CLLocationManagerProtocol { var delegate: CLLocationManagerDelegate?
var monitoredRegions: Set<CLRegion> = Set<CLRegion>() func startMonitoring(for region: CLRegion) { monitoredRegions.insert(region) } func simulateLocation(location: CLLocation) { for region in monitoredRegions { if let circularRegion = region as? CLCircularRegion, circularRegion.contains(location.coordinate) { delegate?.locationManager?(CLLocationManager(), didEnterRegion: region) } } } } Carsten Könemann, Software Engineer @ hmmh 34
Example 3 CoreLocation class MyCoreLocationControllerTests: XCTestCase { func testExample01() {
let controller = MyCoreLocationController() let mockLocationManager = CLLocationManagerMock() controller.locationManager = mockLocationManager mockLocationManager.simulateLocation(location: CLLocation(latitude: 1, longitude: 1)) XCTAssertFalse(controller.didVisitRegion) mockLocationManager.simulateLocation(location: CLLocation(latitude: 0, longitude: 0)) XCTAssertTrue(controller.didVisitRegion) } } Carsten Könemann, Software Engineer @ hmmh 35
Thanks for your attention! Follow us on Twitter: @cargath @hmmh_de
Carsten Könemann, Software Engineer @ hmmh 36