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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
The Rails Doctrine Decade
koic
2
590
KiroのSpecで「五目並べ」を作ってみる
satoshi256kbyte
1
380
個人開発者よ、Honoを使え(おじさんver)
tanahiro2010
2
260
[2026-09-26]空論ジェネリックプロセス~テスト資産とAIで紡ぐ、再現可能なパフォーマンスチューニングの話~
tosite
0
270
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
370
Vue Fes Japan の “作る側”に関わってみて
naokihaba
0
160
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
270
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
390
Rでドレミ/Do-Re-Mi_with_R
florets1
0
130
AmebaブログはKMPから卒業します
kmars0605
0
190
S3 TablesのIcebergデータに、AIエージェントからアクセスしてみた / 20261016developersio2026joetsu-accessing-iceberg-in-s3tables-via-agent
kasacchiful
PRO
0
150
Apple Intelligence を用いた個人情報誤送信防止、及びユーザーリクエスト体験の改善について
yukiny
0
350
Featured
See All Featured
Fantastic passwords and where to find them - at NoRuKo
philnash
53
4k
The Cost Of JavaScript in 2023
addyosmani
55
10k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.5k
Why Our Code Smells
bkeepers
PRO
340
58k
Reality Check: Gamification 10 Years Later
codingconduct
0
2.3k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
What's in a price? How to price your products and services
michaelherold
247
13k
Bootstrapping a Software Product
garrettdimon
PRO
306
120k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3.1k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
33
5.2k
Six Lessons from altMBA
skipperchong
29
4.5k
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