Skip to content

Rightpoint/FRY

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Important Notice

This repository is slated for deletion, perhaps as soon as June 1, 2016. Please find other solutions.

FRY: A UIKit Interaction Library

FRY is an iOS Test Driver. The purpose of this library is to simplify iteractions with UIKit. FRY does this by providing a clear API to perform view lookup, touch interaction, typing, and idle detection.

FRY also includes a library, FRYolator, which helps create integration tests when it is included in your target. The goal of FRYolator is to record all input to the target for later recreation. FRYolator will currently record and generate unit test commands to playback touch events, as well as Network Requests via OHHTTPStubs or Nocilla.

Lookup and Perform

FRY commands usually consist of two parts, the lookup, which finds something on the screen to interact with, and the action -- a touch, swipe, scroll, or type event.

// Tap the button "Share" button on row 5
FRY.lookup(atSectionAndRow(0, 5)).lookup(accessibilityLabel(@"Share")).tap();

// Return all the image views that are inside a UITableViewCell
FRY.lookup(ofKind([UITableViewCell class])).lookup(ofKind([UIImageView class]));

// Tap the OK button inside the alert view
FRY.lookup(ofKind([UIAlertView class])).lookup(accessibilityLabel(@"OK")).tap();

Not all commands require a lookup, since they will implicitly look for the required subview before acting.

// Select the text in the first UITextField
FRY.selectText();

// Scroll down to row 9 in section 0 of the first UIScrollView
FRY.searchFor(FRYDirectionDown, atSectionAndRow(0, 9));

// Select the '200' and 'lbs' on the first UIPickerView
FRY.selectPicker(@"200", 0).selectPicker(@"lbs", 1);

Those commands can also be more specific if a lookup is specified

// Select '200' and 'lbs' on the picker view inside of row 1 (skipping the picker view in row 0)
FRY.lookup(atSectionAndRow(0, 1)).selectPicker(@"200", 0).selectPicker(@"lbs", 1);

More Information on Queries or the Lookup Implementation

Predicates

FRY provides some helpful macros to create compiler-checked predicates. There is a keypath predicate helper FRY_PREDICATE_KEYPATH, and selector predicate helper FRY_PREDICATE_SELECTOR. These predicates will make sure that the keypath or selector is valid, and add a isKindOf: check for safety. There are also a number short-hand helpers in FRYDefines.h for more common lookups like ofKind(), atSectionAndRow(), and accessibilityLabel().

Touch Synthesis

FRY uses strongly modeled touches to generate UIKit touch events. This allows for simple arbitrary touch creation, and clear API's for creating common touch sequences.

FRY.lookup(@"My Label").tap();
FRY.lookup(@"My Label").touch([FRYTouch doubleTap]);
FRY.lookup(@"My Label").touch([FRYTouch dragFromPoint:p1 toPoint:p2 forDuration:1]);

More Information on Touch Synthesis

Typing

Typing is a separate API from FRYQuery, since it doesn't actually lookup any views, it just directly interacts with private API's.

[FRYTypist typeString:@"Fake Input"]

FRY will work with both the hardware and software keyboard, but the software keyboard is much slower.

Idle Detection

There is a helper FRYIdleCheck that will spin the runloop until all touches are finished and all UI animations have completed. This greatly simplifies flow control, and eliminiates the vast majority of CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, false). If you find yourself wanting to add these to your tests, consider adding an animation to your UI instead, or providing additional application-specific checks to the delegate. All FRYQuery actions will call FRYIdleCheck before and after the various actions.

FRYolator

FRYolator will be enabled when a special tap sequence is performed. Once enabled, all touch and network activity will be recorded until the tap sequence is performed again. This will save all input events that FRYolator can record into a .fry file. Inside the .fry bundle are all of the touch events and network communication.

To Enable Monitoring

#ifdef DEBUG
[[FRYolatorUI shared] registerGestureEnablingOnView:self.window];
#endif

FRY will attempt to use relative coordinates with accessibility lookup information for the view that was touched. This will maximize the reliability of these commands when UI changes occur. If no accessibility information can be found, absoulte screen coordinates will be used.

Touch Visualization

FRYolator can also visualize touch events. This is used by FRYolator to indicate that recording is active, but can also be easily enabled in the automated tests to aid in writing tests.

To Enable Touch Visualization

[[FRYTouchHighlightWindowLayer shared] enable];

Touch visualization is automatically enabled by FRYTouchDispatch if the FRYolator library is linked into the target.

iOS Support

iOS 8+ is being actively supported and tested. All of the touch dispatch code should work on any version of iOS, and the typing code should also work on earlier versions of iOS.

Installation

Add FRY to your Podfile to install. If you want to use touch recording, add FRYolator to your application target. Only add it to the 'Debug' configuration to ensure it is not submitted to the app store, or add it to a debug target.

Podfile with Debug configuration

target :YourTests do
  pod 'FRY'
#  Un-comment for touch visualization
#  pod 'FRY/FRYolator'
end

target :YourApplication do
  pod 'FRY/FRYolator', :configuration => "Debug"
end

This will cause your test target to link with FRY, and link your application with FRYolator on Debug configurations

Influences

KIF

KIF is amazing framework that basically started UI testing on iOS. The core design difference with KIF that pushed me to write FRY without looking to maintain compatibility is that looking up a view can modify the view heirarchy to find it. This causes a lot of bizarre issues, and it is not usually what I want from a testing perspective.

Robot

Robot is a newer contender in the UI Testing world, and is very interesting, with a primary goal of speed.