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

Cocoaheads Montréal : Automated testing for iOS

Cocoaheads Montréal : Automated testing for iOS

Cocoaheads Montréal - 11/02/2014

Romain Pouclet

February 12, 2014
Tweet

More Decks by Romain Pouclet

Other Decks in Programming

Transcript

  1. Automated Testing
    (Cocoaheads Montréal - 11/02/2014)

    View full-size slide

  2. Who ?
    ▸ Romain Pouclet
    ▸ iOS developer
    ▸ Building stuff at
    TechSolCom

    View full-size slide

  3. How many developer
    in the room?

    View full-size slide

  4. How many of you have
    already written a unit
    test case?

    View full-size slide

  5. How many of you have
    already written a
    functional test case ?

    View full-size slide

  6. The streching session
    is over, you can go
    back to your beer

    View full-size slide

  7. Reasons to
    test

    View full-size slide

  8. Reasons to test
    ▸ Make sure everything work as expected
    ▸ Avoid regression
    ▸ Natural documentation
    ▸ Ease maintenance and refactoring
    ▸ Sleep better at night

    View full-size slide

  9. Reasons not
    to test

    View full-size slide

  10. Reasons not to test
    Prototyping
    Deadlines
    Testing is hard
    I don't need them, I know what I'm doing

    View full-size slide

  11. Reasons not to test
    Prototyping
    Deadlines
    Testing is hard
    I don't need them, I know what I'm doing

    View full-size slide

  12. Reasons not to test
    Prototyping
    Deadlines
    Testing is hard
    I don't need them, I know what I'm doing

    View full-size slide

  13. Reasons not to test
    Prototyping
    Deadlines
    Testing is hard
    I don't need them, I know what I'm doing

    View full-size slide

  14. Example
    Parsing some XML

    View full-size slide

  15. Example : XML parsing
    ▸ Augmented reality application using Vuforia
    ▸ Metadata stored here (Vuforia) and there (Client
    database)

    View full-size slide

  16. Example : XML parsing


    1
    Harry Potter and the goblet of fire



    2
    Harry Potter and the Deathly Hallows




    View full-size slide

  17. Dead simple XML parsing
    PCSGetBookDetailsParserDelegate *parsingDelegate = [[PCSGetBookDetailsParserDelegate alloc] init];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL: url];
    parser.delegate = parsingDelegate;
    BOOL result = [parser parse];
    PCSBook *book = parsingDelegate.book;

    View full-size slide

  18. The developer (not me, another nice guy) didn't write any
    test for this

    View full-size slide

  19. "There are too much metadata on Vuforia, please only use
    item and image data and fetch the rest from the
    backoffice"

    View full-size slide

  20. Sure!
    [[PCSGetBookDetailsParserDelegate alloc] initWithWhiteList: @[@"imagedata", @"item"]];

    View full-size slide

  21. There is a bug in the application
    when the user search for a book
    on a tuesday
    — A (nice) client

    View full-size slide

  22. Achievement
    unlocked !
    humility

    View full-size slide

  23. Write a failing test case
    - (void)testItemIdIsNotOverridenByRecommandationItemId {
    NSURL *url = [[NSBundle bundleForClass: [self class]] URLForResource: @"342279" withExtension: @"xml"];
    NSAssert(url, @"URL for sample payload should be properly loadded");
    PCSGetBookDetailsParserDelegate *parsingDelegate = [[PCSGetBookDetailsParserDelegate alloc] initWithWhiteList: @[@"imagedata", @"item"]];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL: url];
    parser.delegate = parsingDelegate;
    BOOL result = [parser parse];
    NSAssert(result, @"Payload shoud be successfully parsed.");
    PCSBook *book = parsingDelegate.book;
    XCTAssertEqualObjects(@"342279", book.item);
    }

    View full-size slide

  24. Failing test case : load data you know
    NSURL *url = [[NSBundle bundleForClass: [self class]] URLForResource: @"342279" withExtension: @"xml"];
    NSAssert(url, @"URL for sample payload should be properly loadded");

    View full-size slide

  25. Failing test case : do the actual parsing
    PCSGetBookDetailsParserDelegate *parsingDelegate = [[PCSGetBookDetailsParserDelegate alloc] initWithWhiteList: @[@"imagedata", @"item"]];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL: url];
    parser.delegate = parsingDelegate;
    BOOL result = [parser parse];
    NSAssert(result, @"Payload shoud be successfully parsed.");

    View full-size slide

  26. Failing test case : add your assertions
    PCSBook *book = parsingDelegate.book;
    XCTAssertEqualObjects(@"342279", book.item);

    View full-size slide

  27. Run your code
    ⌘ + U
    or
    xcodebuild test

    View full-size slide

  28. Or... Xctool (facebook)
    $ brew install xctool
    $ xctool -project XMLParsing.xcodeproj -scheme XMLParsing test

    View full-size slide

  29. Or... Xcodebuild and XCPretty
    $ gem install xcpretty
    $ xcodebuild test | xcpretty

    View full-size slide

  30. The secret of happiness*
    1. Find a bug
    2. Write a (failing) test case
    3. Fix the bug
    4. Repeat
    (* not really)

    View full-size slide

  31. Unit testing
    Test the smallest part of an application (= unit)

    View full-size slide

  32. A good test
    suite is...

    View full-size slide

  33. Easy to read
    - (void)testSomethingShouldHappenThisWay {
    Stuff *someStuff = [[Stuff alloc] init];
    XCTAssertEqualObjects(@"Expected result", [someStuff performSomeAction], @"The action should do something")
    }

    View full-size slide

  34. Separation of concern
    Don't duplicate your checks : higher-level tests assume
    that lower-level code works.

    View full-size slide

  35. Avoid singletons
    @implementation PCSSuperImportantObject
    - (instancetype)init {
    self = [super init];
    if (self) {
    self.connection = [PCSTwitterConnection sharedConnection];
    }
    return self;
    }
    @end

    View full-size slide

  36. Use dependency injection!
    @implementation PCSSuperImportantObject
    - (instancetype)initWithConnection:(PCSTwitterConnection *)connection {
    self = [super init];
    if (self) {
    self.connection = connection;
    }
    return self;
    }
    @end

    View full-size slide

  37. ... So you can mock them later
    id connection = [OCMockObject mockForClass: [PCSTwitterConnection class]];
    [[[connection stub] andReturn: @367] followerCountsForUsername: [OCMArg any]];
    NSLog(@"Followers = %@", [connection followerCountsForUsername: @"CocoaheadsMTL"]);

    View full-size slide

  38. Don't test everything
    HSWizard *harry = [[HSWizard alloc] init];
    harry.house = @"Gryffindor";
    XCTAssertEqualsObject(@"Gryffindor", harry.house);

    View full-size slide

  39. But wait
    there's more !

    View full-size slide

  40. Acceptance testing using KIF
    - (void)testSuccessfulLogin {
    [tester enterText: @"https://confluence-url.com" intoViewWithAccessibilityLabel: @"Confluence URL"];
    [tester enterText: @"apple.review" intoViewWithAccessibilityLabel: @"Confluence username"];
    [tester enterText: @"applereview" intoViewWithAccessibilityLabel: @"Confluence password"];
    [tester tapViewWithAccessibilityLabel: @"Log In"];
    // Verify that the login succeeded
    [tester waitForTappableViewWithAccessibilityLabel: @"Spaces list"];
    [tester tapRowInTableViewWithAccessibilityLabel: @"Spaces list" atIndexPath: [NSIndexPath indexPathForRow: 0 inSection: 0]];
    [tester waitForTappableViewWithAccessibilityLabel: @"Main menu"];
    }

    View full-size slide

  41. Acceptance testing using Frank
    Feature:
    As a user
    I want to be able to see the weather of a region
    So I can see if I need to bring flip flops or a snow shovel
    Scenario: I need to be able to select a city
    Given I launch the app
    And I touch "Select weather city"
    And I wait to see "Alma"
    And I touch "Alma"
    Then I should see "Alma"
    $ cucumber features/weather.feature

    View full-size slide

  42. It is not about testing everything
    It is about raising your level of confidence in your code

    View full-size slide

  43. Thank you!
    (Questions ?)

    View full-size slide