iosobjective-cxcodexctestearlgrey

Randomly Selecting In EarlGrey


I was writing pretty complicated UI tests using XCTest, and recently switched to EarlGrey, because it's so much faster and way more reliable - Tests aren't randomly failing on a build server, and the test suite could take up to a half hour to run!

One thing that I haven't been able to do in EarlGrey, that I could do in XCTest, was randomly select an element.

For example, on a calendar collectionView, I could query for all collectionViewCells with 'identifier' using an NSPredicate, and then randomly select a day using [XCUIElementQuery count] to get an index, and then tap.

For now, I'm going to hard code it, but I would love to randomize date selection so that I don't have to rewrite tests if we change app code.

Please let me know if I can elaborate, looking forward to solving this!


Solution

  • Step 1 write a matcher that can count elements matching a given matcher using GREYElementMatcherBlock:

    - (NSUInteger)elementCountMatchingMatcher:(id<GREYMatcher>)matcher {
      __block NSUInteger count = 0;
      GREYElementMatcherBlock *countMatcher = [GREYElementMatcherBlock matcherWithMatchesBlock:^BOOL(id element) {
        if ([matcher matches:element]) {
          count += 1;
        }
        return NO; // return NO so EarlGrey continues to search.
      } descriptionBlock:^(id<GREYDescription> description) {
        // Pass
      }];
      NSError *unused;
      [[EarlGrey selectElementWithMatcher:countMatcher] assertWithMatcher:grey_notNil() error:&unused];
      return count;
    }
    

    Step 2 Use % to select a random index

    NSUInteger randomIndex = arc4random() % count;
    

    Step 3 Finally use atIndex: to select that random element and perform action/assertion on it.

    // Count all UIView's
    NSUInteger count = [self elementCountMatchingMatcher:grey_kindOfClass([UIView class])];
    // Find a random index.
    NSUInteger randIndex = arc4random() % count;
    // Tap the random UIView
    [[[EarlGrey selectElementWithMatcher:grey_kindOfClass([UIView class])]
        atIndex:randIndex]
        performAction:grey_tap()];