Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Protocol-Oriented Testing in Swift
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
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
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
260
Signal Forms: Details & Live Coding @enterJS 2026 in Mannheim
manfredsteyer
PRO
0
150
並列実装の現場、2ヶ月間実務でAIを使い倒したAIもPCも私も限界が近い
ming_ayami
0
130
jQueryをバージョンアップする前に使いたいjQuery Migrate
matsuo_atsushi
0
560
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
6.6k
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
4
1.4k
Contextとはなにか
chiroruxx
1
330
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.6k
Go1.27で導入されるジェネリクスメソッドでできること
mackee
0
140
Honoでのサプライチェーン侵害対策 〜 3つのライブラリに学ぶ
yusukebe
6
1.3k
Spec Driven Development | AI Summit Lisbon
danielsogl
PRO
0
200
軽量Java基盤の設計 DIコンテナに頼らない、長期保守と1秒起動の実現 JJUG CCC 2026 Spring
macha64
0
540
Featured
See All Featured
How to train your dragon (web standard)
notwaldorf
97
6.7k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
320
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
430
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
230
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Large-scale JavaScript Application Architecture
addyosmani
515
110k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
610
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
200
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.8k
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.3k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
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