Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing on iOS

AppFoundry
December 10, 2015

Testing on iOS

A presentation by Joris Dubois (AppFoundry) for the December 2015 Mobel user group meetup

AppFoundry

December 10, 2015
Tweet

More Decks by AppFoundry

Other Decks in Technology

Transcript

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

    View Slide

  2. AppFoundry
    appfoundry.be

    View Slide

  3. View Slide

  4. Patterns

    View Slide

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

    View Slide

  6. 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

    View Slide

  7. 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

    View Slide

  8. 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

    View Slide

  9. 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

    View Slide

  10. MVVM

    View Slide

  11. MVC

    View Slide

  12. MASSIVE
    View Controller

    View Slide

  13. View Controller
    View Model
    MVVM
    View Controller
    View Model
    View Model

    View Slide

  14. Unit Testing
    frameworks & code coverage

    View Slide

  15. OCMock

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. OCMockito
    personal favorite

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. Expecta

    View Slide

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

    View Slide

  27. 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)

    View Slide

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

    View Slide

  29. OCHamcrest
    personal favorite

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  33. Specta

    View Slide

  34. 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

    View Slide

  35. Code coverage
    live demo

    View Slide

  36. UI Testing

    View Slide

  37. UI testing in Xcode
    live demo

    View Slide

  38. KIF
    live demo

    View Slide

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

    View Slide

  40. User feedback

    View Slide

  41. A/B testing

    View Slide

  42. View Slide

  43. View Slide

  44. B
    A

    View Slide

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

    View Slide