Slide 1

Slide 1 text

Your hosts Filip Maelbrancke Consultant @ AppFoundry fi[email protected] @fmaelbrancke Joris Dubois Consultant @ AppFoundry [email protected] @DuboisJoris

Slide 2

Slide 2 text

AppFoundry appfoundry.be

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Patterns

Slide 5

Slide 5 text

Dependency Injection a 25-dollar term for a 5-cent concept

Slide 6

Slide 6 text

Coding Example DI: Spot the problem #import @interface UserDefaultManager : NSObject - (void)registerLeetInteger; @end #import "UserDefaultManager.h" @implementation UserDefaultManager - (void)registerLeetInteger { [[[NSUserDefaults alloc] initWithSuiteName:@"LeetAppGroup"] setInteger:1337 forKey:@"leet"]; } @end

Slide 7

Slide 7 text

Coding Example DI: Constructor Injection #import @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

Slide 8

Slide 8 text

Coding Example DI: Method Injection #import @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

Slide 9

Slide 9 text

Coding Example DI: Property Injection #import @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

Slide 10

Slide 10 text

MVVM

Slide 11

Slide 11 text

MVC

Slide 12

Slide 12 text

MASSIVE View Controller

Slide 13

Slide 13 text

View Controller View Model MVVM View Controller View Model View Model

Slide 14

Slide 14 text

Unit Testing frameworks & code coverage

Slide 15

Slide 15 text

OCMock

Slide 16

Slide 16 text

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()

Slide 17

Slide 17 text

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);

Slide 18

Slide 18 text

Coding Example OCMock: Verification OCMVerify([mock someMethod]);

Slide 19

Slide 19 text

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]); }

Slide 20

Slide 20 text

OCMockito personal favorite

Slide 21

Slide 21 text

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));

Slide 22

Slide 22 text

Coding Example OCMockito: Stubbing [given([mock someMethod]) willReturn:anObject]; [given([mock aMethodReturningABoolean]) willReturnBool:YES];

Slide 23

Slide 23 text

Coding Example OCMockito: Verification [verify(mock) someMethod]; [verifyCount(mock, times(1)) someMethod]; [verifyCount(mock, never()) someMethod];

Slide 24

Slide 24 text

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]; }

Slide 25

Slide 25 text

Expecta

Slide 26

Slide 26 text

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()

Slide 27

Slide 27 text

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)

Slide 28

Slide 28 text

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); }

Slide 29

Slide 29 text

OCHamcrest personal favorite

Slide 30

Slide 30 text

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()

Slide 31

Slide 31 text

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()

Slide 32

Slide 32 text

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)); }

Slide 33

Slide 33 text

Specta

Slide 34

Slide 34 text

Coding Example Specta SpecBegin(_PersonViewPopulator) describe(@"PersonViewPopulator", ^{ __block PersonViewPopulator *_populator; __block PersonObject *_data; __block PersonView *_view; __block id _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

Slide 35

Slide 35 text

Code coverage live demo

Slide 36

Slide 36 text

UI Testing

Slide 37

Slide 37 text

UI testing in Xcode live demo

Slide 38

Slide 38 text

KIF live demo

Slide 39

Slide 39 text

Clean Code - Robert C. Martin Test-Driven iOS Development - Graham Lee Test-Driven Development by Example - Kent Beck

Slide 40

Slide 40 text

User feedback

Slide 41

Slide 41 text

A/B testing

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

B A

Slide 45

Slide 45 text

Questions? Joris Dubois Consultant @ AppFoundry [email protected] @DuboisJoris