Cocoaheads Montréal : Automated testing for iOS

Cocoaheads Montréal : Automated testing for iOS

Cocoaheads Montréal - 11/02/2014

B4f9306896b6eaa56a9a6b9048285f53?s=128

Romain Pouclet

February 12, 2014
Tweet

Transcript

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

  2. Who ? ▸ Romain Pouclet ▸ iOS developer ▸ Building

    stuff at TechSolCom
  3. How many developer in the room?

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

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

    case ?
  6. The streching session is over, you can go back to

    your beer
  7. Reasons to test

  8. Reasons to test ▸ Make sure everything work as expected

    ▸ Avoid regression ▸ Natural documentation ▸ Ease maintenance and refactoring ▸ Sleep better at night
  9. Reasons not to test

  10. Reasons not to test Prototyping Deadlines Testing is hard I

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

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

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

    don't need them, I know what I'm doing
  14. Example Parsing some XML

  15. Example : XML parsing ▸ Augmented reality application using Vuforia

    ▸ Metadata stored here (Vuforia) and there (Client database)
  16. Example : XML parsing <?xml version="1.0" encoding="utf-8" ?> <book> <book_id>1</book_id>

    <title>Harry Potter and the goblet of fire</title> <!-- Other nodes --> <recommandations> <book> <book_id>2</book_id> <title>Harry Potter and the Deathly Hallows</title> </book> <!-- MORE BOOKS --> </recommandations> </book>
  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;
  18. The developer (not me, another nice guy) didn't write any

    test for this
  19. "There are too much metadata on Vuforia, please only use

    item and image data and fetch the rest from the backoffice"
  20. Sure! [[PCSGetBookDetailsParserDelegate alloc] initWithWhiteList: @[@"imagedata", @"item"]];

  21. Guess what?

  22. There is a bug in the application when the user

    search for a book on a tuesday — A (nice) client
  23. Achievement unlocked ! humility

  24. 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); }
  25. 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");
  26. 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.");
  27. Failing test case : add your assertions PCSBook *book =

    parsingDelegate.book; XCTAssertEqualObjects(@"342279", book.item);
  28. Run your code ⌘ + U or xcodebuild test

  29. Or... Xctool (facebook) $ brew install xctool $ xctool -project

    XMLParsing.xcodeproj -scheme XMLParsing test
  30. Or... Xcodebuild and XCPretty $ gem install xcpretty $ xcodebuild

    test | xcpretty
  31. The secret of happiness* 1. Find a bug 2. Write

    a (failing) test case 3. Fix the bug 4. Repeat (* not really)
  32. Unit testing Test the smallest part of an application (=

    unit)
  33. A good test suite is...

  34. Easy to read - (void)testSomethingShouldHappenThisWay { Stuff *someStuff = [[Stuff

    alloc] init]; XCTAssertEqualObjects(@"Expected result", [someStuff performSomeAction], @"The action should do something") }
  35. Fast to run

  36. Separation of concern Don't duplicate your checks : higher-level tests

    assume that lower-level code works.
  37. Avoid singletons @implementation PCSSuperImportantObject - (instancetype)init { self = [super

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

    = [super init]; if (self) { self.connection = connection; } return self; } @end
  39. ... So you can mock them later id connection =

    [OCMockObject mockForClass: [PCSTwitterConnection class]]; [[[connection stub] andReturn: @367] followerCountsForUsername: [OCMArg any]]; NSLog(@"Followers = %@", [connection followerCountsForUsername: @"CocoaheadsMTL"]);
  40. Don't test everything HSWizard *harry = [[HSWizard alloc] init]; harry.house

    = @"Gryffindor"; XCTAssertEqualsObject(@"Gryffindor", harry.house);
  41. But wait there's more !

  42. 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"]; }
  43. 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
  44. It is not about testing everything It is about raising

    your level of confidence in your code
  45. Thank you! (Questions ?)