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
Testing on iOS
Search
AppFoundry
December 10, 2015
Technology
0
270
Testing on iOS
A presentation by Joris Dubois (AppFoundry) for the December 2015 Mobel user group meetup
AppFoundry
December 10, 2015
Tweet
Share
More Decks by AppFoundry
See All by AppFoundry
Introductie iOS - Jens
appfoundrybe
0
97
Android In Practice
appfoundrybe
0
140
Android Introduction 3.0 by Siebe
appfoundrybe
0
110
Android in Practice (long)
appfoundrybe
0
200
React Native - cross-platform mobile app development
appfoundrybe
0
170
React Native Storybook
appfoundrybe
0
450
the ionic crash course
appfoundrybe
1
180
View based apps with Conductor
appfoundrybe
0
630
Android Accessibility at GDG Devfest Brussels 2016
appfoundrybe
0
570
Other Decks in Technology
See All in Technology
2025年になってもまだMySQLが好き
yoku0825
8
4.5k
なぜSaaSがMCPサーバーをサービス提供するのか?
sansantech
PRO
8
2.7k
機械学習を扱うプラットフォーム開発と運用事例
lycorptech_jp
PRO
0
220
フルカイテン株式会社 エンジニア向け採用資料
fullkaiten
0
8.7k
これでもう迷わない!Jetpack Composeの書き方実践ガイド
zozotech
PRO
0
270
「何となくテストする」を卒業するためにプロダクトが動く仕組みを理解しよう
kawabeaver
0
280
現場で効くClaude Code ─ 最新動向と企業導入
takaakikakei
1
200
「全員プロダクトマネージャー」を実現する、Cursorによる仕様検討の自動運転
applism118
10
4.4k
なぜテストマネージャの視点が 必要なのか? 〜 一歩先へ進むために 〜
moritamasami
0
200
Skrub: machine-learning with dataframes
gaelvaroquaux
0
120
人工衛星のファームウェアをRustで書く理由
koba789
11
6.5k
バイブスに「型」を!Kent Beckに学ぶ、AI時代のテスト駆動開発
amixedcolor
2
500
Featured
See All Featured
Building a Scalable Design System with Sketch
lauravandoore
462
33k
Done Done
chrislema
185
16k
How GitHub (no longer) Works
holman
315
140k
How STYLIGHT went responsive
nonsquared
100
5.8k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
23
1.4k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.4k
Agile that works and the tools we love
rasmusluckow
330
21k
Docker and Python
trallard
45
3.6k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
15k
Gamification - CAS2011
davidbonilla
81
5.4k
Testing 201, or: Great Expectations
jmmastey
45
7.6k
Making Projects Easy
brettharned
117
6.4k
Transcript
Your hosts Filip Maelbrancke Consultant @ AppFoundry fi
[email protected]
@fmaelbrancke Joris
Dubois Consultant @ AppFoundry
[email protected]
@DuboisJoris
AppFoundry appfoundry.be
None
Patterns
Dependency Injection a 25-dollar term for a 5-cent concept
Coding Example DI: Spot the problem #import <Foundation/Foundation.h> @interface UserDefaultManager
: NSObject - (void)registerLeetInteger; @end #import "UserDefaultManager.h" @implementation UserDefaultManager - (void)registerLeetInteger { [[[NSUserDefaults alloc] initWithSuiteName:@"LeetAppGroup"] setInteger:1337 forKey:@"leet"]; } @end
Coding Example DI: Constructor Injection #import <Foundation/Foundation.h> @interface UserDefaultManager :
NSObject - (instancetype)initWithUserDefaults:(NSUserDefaults *)userDefaults; - (void)registerLeetInteger; @end #import "UserDefaultManager.h" @implementation UserDefaultManager { NSUserDefaults *_userDefaults; } //Constructor Injection - (instancetype)initWithUserDefaults:(NSUserDefaults *)userDefaults { self = [super init]; if(self) { _userDefaults = userDefaults; } return self; } - (void)registerLeetInteger { [_userDefaults setInteger:1337 forKey:@"leet"]; } @end
Coding Example DI: Method Injection #import <Foundation/Foundation.h> @interface UserDefaultManager :
NSObject - (void)registerLeetIntegerOnUserDefaults:(NSUserDefaults *)userDefaults; @end #import "UserDefaultManager.h" @implementation UserDefaultManager //Method Injection - (void)registerLeetIntegerOnUserDefaults:(NSUserDefaults *)userDefaults { [userDefaults setInteger:1337 forKey:@"leet"]; } @end
Coding Example DI: Property Injection #import <Foundation/Foundation.h> @interface UserDefaultManager :
NSObject //Property Injection @property (nonatomic, strong) NSUserDefaults *userDefaults; - (void)registerLeetInteger; @end #import "UserDefaultManager.h" @implementation UserDefaultManager - (void)registerLeetInteger { [self.userDefaults setInteger:1337 forKey:@"leet"]; } @end
MVVM
MVC
MASSIVE View Controller
View Controller View Model MVVM View Controller View Model View
Model
Unit Testing frameworks & code coverage
OCMock
Coding Example OCMock: Mocking Class mock OCMClassMock([SomeClass class]); Protocol mock
OCMProtocolMock(@protocol(SomeProtocol)); Strict class & protocol mock OCMStrictClassMock([SomeObject class]); OCMStrictProtocolMock(@protocol(SomeProtocol)); Partial mock OCMPartialMock(anObject); Observer mock OCMObserverMock()
Coding Example OCMock: Stubbing OCMStub([mock someMethod]).andReturn(anObject); OCMStub([mock someMethodReturningABool]).andReturn(YES); OCMStub([mock someMethod]).andCall(anotherObject,
@selector(aDifferentMethod)); OCMStub([mock someMethod]).andThrow(anException);
Coding Example OCMock: Verification OCMVerify([mock someMethod]);
Coding Example OCMock - (void)populateView:(UIView *)view withData:(id)data { PersonView *personView
= (PersonView *)view; PersonObject *personObject = (PersonObject *)data; [_imageService fetchImageFromURL:personObject.imageURL fallBackImage:[UIImage personFallBackImage] forImageView:personView.personImageView]; } - (void)testPopulateViewWithDataDoesInvokeImageServiceForPersonImageViewWithExpectedFallbackImage { UIImageView *givenImageView = [[UIImageView alloc] init]; OCMExpect(_view.personImageView).andReturn(givenImageView); [_populator populateView:_view withData:_data]; OCMVerify([_imageService fetchImageFromURL:_data.imageURL fallBackImage:[UIImage personFallBackImage] forImageView:givenImageView]); }
OCMockito personal favorite
Coding Example OCMockito: Mocking Class mock mockClass([SomeClass class]); Protocol mock
mockProtocol(@protocol(SomeProtocol)); mockProtocolWithoutOptionals(@protocol( SomeProtocol)); Class & protocol mock mockObjectAndProtocol([SomeObject class], @protocol(SomeProtocol));
Coding Example OCMockito: Stubbing [given([mock someMethod]) willReturn:anObject]; [given([mock aMethodReturningABoolean]) willReturnBool:YES];
Coding Example OCMockito: Verification [verify(mock) someMethod]; [verifyCount(mock, times(1)) someMethod]; [verifyCount(mock,
never()) someMethod];
Coding Example OCMockito - (void)populateView:(UIView *)view withData:(id)data { PersonView *personView
= (PersonView *)view; PersonObject *personObject = (PersonObject *)data; [_imageService fetchImageFromURL:personObject.imageURL fallBackImage:[UIImage personFallBackImage] forImageView:personView.personImageView]; } - (void)testPopulateViewWithDataDoesInvokeImageServiceForPersonImageViewWithExpectedFallbackImage { UIImageView *givenImageView = [[UIImageView alloc] init]; [given(_view.personImageView) willReturn:givenImageView]; [_populator populateView:_view withData:_data]; [verify(_imageService) fetchImageFromURL:_data.imageURL fallBackImage:[UIImage personFallBackImage] forImageView:givenImageView]; }
Expecta
Coding Example Expecta Object matchers .conformsTo(@protocol(SomeProtocol)) .equal(anObject) .beIdenticalTo(anObject) .beNil() .beInstanceOf([SomeClass
class]) Number matchers .beCloseTo(@3) .beGreaterThan(@3) .beGreaterThanOrEqualTo(@3) .beLessThan(@3) .beLessThanOrEqualTo(@3) .beFalsy() .beTruthy()
Coding Example Expecta Text matchers .beginWith(@"someString") .endWith(@"someString") .match(@"regex") Collection matchers
.contain(objectA, objectB) .beSupersetOf(objectA, objectB) .haveCountOf() .beEmpty() Logical matchers .notTo Async matchers .will .willNot .after(2)
Coding Example Expecta - (void)populateView:(UIView *)view withData:(id)data { PersonView *personView
= (PersonView *)view; PersonObject *personObject = (PersonObject *)data; personView.ageLabel.text = personObject.age.stringValue; } - (void)testPopulateViewWithDataDoesPopulateAgeLabelWithAge { UILabel *givenLabel = [[UILabel alloc] init]; OCMExpect(_view.ageLabel).andReturn(givenLabel); [_populator populateView:_view withData:_data]; expect(givenLabel.text).to.equal(_data.age.stringValue); }
OCHamcrest personal favorite
Coding Example OCHamcrest Object matchers conformsTo(@protocol(SomeProtocol)) equalTo(anObject) sameInstance(anObject) hasProperty(@"propertyName", @“propertyValue")
instanceOf([SomeClass class]) Number matchers closeTo(@3) greaterThan(@3) greaterThanOrEqualTo(@3) lessThan(@3) lessThanOrEqualTo(@3) isFalse() isTrue()
Coding Example OCHamcrest Text matchers containsSubstring(@"someString"); startsWith(@"someString"); endsWith(@"someString"); equalToIgnoringCase(@"someString"); Collection
matchers contains(objectA, objectB) hasItems(objectA, objectB) everyItem() isEmpty() Logical matchers isNot() anything() allOf()
Coding Example OCHamcrest - (void)populateView:(UIView *)view withData:(id)data { PersonView *personView
= (PersonView *)view; PersonObject *personObject = (PersonObject *)data; personView.ageLabel.text = personObject.age.stringValue; } - (void)testPopulateViewWithDataDoesPopulateAgeLabelWithAge { UILabel *givenLabel = [[UILabel alloc] init]; [given(_view.ageLabel) willReturn:givenLabel]; [_populator populateView:_view withData:_data]; assertThat(givenLabel.text, is(equalTo(_data.age.stringValue)); }
Specta
Coding Example Specta SpecBegin(_PersonViewPopulator) describe(@"PersonViewPopulator", ^{ __block PersonViewPopulator *_populator; __block
PersonObject *_data; __block PersonView *_view; __block id<ImageService> _imageService; __block NSURL *_imageURL; beforeEach(^{ _imageURL = [[NSURL alloc] initWithString:@"http://www.apple.com"]; _view = OCMClassMock([PersonView class]); _imageService = OCMProtocolMock(@protocol(ImageService)); _populator = [[PersonViewPopulator alloc] initWithImageService:_imageService]; _data = [[PersonObject alloc] initWithName:@"expectedName" imageURL:_imageURL slogan:@"expectedSlogan" location:@"expectedLocation" age:@25]; }); it(@"does invoke image service with expected fallback image", ^{ UIImageView *givenImageView = [[UIImageView alloc] init]; OCMExpect(_view.personImageView).andReturn(givenImageView); [_populator populateView:_view withData:_data]; OCMVerify([_imageService fetchImageFromURL:_data.imageURL fallBackImage:[UIImage personFallBackImage] forImageView:givenImageView]); }); }); SpecEnd
Code coverage live demo
UI Testing
UI testing in Xcode live demo
KIF live demo
Clean Code - Robert C. Martin Test-Driven iOS Development -
Graham Lee Test-Driven Development by Example - Kent Beck
User feedback
A/B testing
None
None
B A
Questions? Joris Dubois Consultant @ AppFoundry
[email protected]
@DuboisJoris