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
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
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
600
「人を評価する AI」の設計と実装
ryoyanara
0
180
その節約、円になってますか?
isamumumu
1
710
yield再入門 #phpcon
o0h
PRO
0
980
【SRE NEXT 2026 Lunch Session】一人目専任SREの立ち上げを加速する ― AIと進めたオンボーディングで2分を0.04秒にした話
pkshadeck
PRO
0
3.6k
AI時代のPHPer生存戦略 ~「言語、もうなんでもよくない?」に本気で向き合う~
vivion
0
300
【やさしく解説 設計編・中級 #4】ルールの寿命と、システムの年輪
panda728
PRO
2
180
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
590
自動化したのに回らないテスト運用の壁ーAI時代の品質責任と生産性
mfunaki
0
130
Claude Code全社展開のためにやったことn選~プラグイン302個・コミッター271人を支えるために~
kenchan
5
1.4k
the container ship “Apple Silicon”@WWDC26 Recap -Japan-\(region).swift
shingangan
0
110
VibeCodingからAgenticWorkflowへ
starfish719
0
330
Featured
See All Featured
A Soul's Torment
seathinner
6
3.4k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
56k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
660
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
520
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.3k
The agentic SEO stack - context over prompts
schlessera
0
860
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.1k
The Cult of Friendly URLs
andyhume
79
7k
The Curious Case for Waylosing
cassininazir
1
450
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
390
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