iOS4时代,UIAutomation是可以在真实设备和iPhone模拟器上执行自动化测试的常用框架.UIAutomation的功能测试代码是用Javascript编写的。UIAutomation和Accessibility有着直接的关系,通过标签和值的访问性来获得UI元素.
UITest是Xcode7.0之后苹果推出的,主要功能也是页面UI测试,相比之前而言,可以通过OC或者Swift更熟悉的语言编写UI测试代码,UIAutomatio已经不被苹果弃用,UITest最大的亮点是支持屏幕录制——通过对App的操作自动生成相应的测试脚本代码.即时我们不懂开发也可以很快写出基本UI测试代码.
基础知识
UITest同样依赖控件的Accessibility属性,UITest为所有UIKit控件提供了一个XCUI开头的代理类。比如UIApplication,对应的是XCUIApplication,在 UI Testing 中代表整个 app 的对象。
对于一般的UIKit对象,Apple提供XCUIElement对象作为映射。我们不能直接通过得到的 XCUIElement 来直接访问被测 app 中的元素,而只能通过 Accessibility 中的像是 identifier 或者 frame 这样的属性来获取 UI 的信息.
UITest核心的类有三个XCUIApplication,XCUIElement 和XCUIElementQuery.
①XCUIApplication是Application的代理,用来启动或者是终止UI测试程序.启动参数设置,在获取程序中的UI元素。
②XCUIElement是XCTest.framework对应用中的所有UI控件的抽象,所有的UI控件都是此类型,滑动,点击,轻扫手势:
`@interface XCUIElement (XCUIElementTouchEvents)
/*!
- Sends a tap event to a hittable point computed for the element.
*/
- (void)tap;
/*!
- Sends a double tap event to a hittable point computed for the element.
*/
- (void)doubleTap;
/*!
- Sends a two finger tap event to a hittable point computed for the element.
*/
- (void)twoFingerTap;
/*!
- Sends one or more taps with one of more touch points.
-
- @param numberOfTaps
- The number of taps.
-
- @param numberOfTouches
- The number of touch points.
*/
- (void)tapWithNumberOfTaps:(NSUInteger)numberOfTaps numberOfTouches:(NSUInteger)numberOfTouches;
/*!
- Sends a long press gesture to a hittable point computed for the element, holding for the specified duration.
-
- @param duration
- Duration in seconds.
*/
- (void)pressForDuration:(NSTimeInterval)duration;
/*!
- Initiates a press-and-hold gesture that then drags to another element, suitable for table cell reordering and similar operations.
- @param duration
- Duration of the initial press-and-hold.
- @param otherElement
- The element to finish the drag gesture over. In the example of table cell reordering, this would be the reorder element of the destination row.
*/
- (void)pressForDuration:(NSTimeInterval)duration thenDragToElement:(XCUIElement *)otherElement;
/*!
- Sends a swipe-up gesture.
*/
- (void)swipeUp;
/*!
- Sends a swipe-down gesture.
*/
- (void)swipeDown;
/*!
- Sends a swipe-left gesture.
*/
- (void)swipeLeft;
/*!
- Sends a swipe-right gesture.
*/
- (void)swipeRight;
/*!
- Sends a pinching gesture with two touches.
-
- The system makes a best effort to synthesize the requested scale and velocity: absolute accuracy is not guaranteed.
- Some values may not be possible based on the size of the element's frame - these will result in test failures.
-
- @param scale
- The scale of the pinch gesture. Use a scale between 0 and 1 to "pinch close" or zoom out and a scale greater than 1 to "pinch open" or zoom in.
-
- @param velocity
- The velocity of the pinch in scale factor per second.
*/
- (void)pinchWithScale:(CGFloat)scale velocity:(CGFloat)velocity;
/*!
- Sends a rotation gesture with two touches.
-
- The system makes a best effort to synthesize the requested rotation and velocity: absolute accuracy is not guaranteed.
- Some values may not be possible based on the size of the element's frame - these will result in test failures.
-
- @param rotation
- The rotation of the gesture in radians.
-
- @param velocity
- The velocity of the rotation gesture in radians per second.
*/
- (void)rotate:(CGFloat)rotation withVelocity:(CGFloat)velocity;
@end
endif // TARGET_OS_IOS
if TARGET_OS_OSX
@interface XCUIElement (XCUIElementMouseEvents)
/*!
- Moves the cursor over the element.
*/
- (void)hover;
/*!
- Sends a click event to a hittable point computed for the element.
*/
- (void)click;
/*!
- Sends a double click event to a hittable point computed for the element.
*/
- (void)doubleClick;
/*!
- Sends a right click event to a hittable point computed for the element.
*/
- (void)rightClick;
/*!
- Clicks and holds for a specified duration (generally long enough to start a drag operation) then drags
- to the other element.
*/
- (void)clickForDuration:(NSTimeInterval)duration thenDragToElement:(XCUIElement *)otherElement;
/*!
- Scroll the view the specified pixels, x and y.
*/
- (void)scrollByDeltaX:(CGFloat)deltaX deltaY:(CGFloat)deltaY;
@end`
XCUIElementAttributes协议描述UI元素的属性:Identity,Value,Interaction State,Size,elementType等属性.
`@protocol XCUIElementAttributes
/*! The accessibility identifier. */
@property (readonly) NSString *identifier;
/*! The frame of the element in the screen coordinate space. */
@property (readonly) CGRect frame;
/*! The raw value attribute of the element. Depending on the element, the actual type can vary. */
@property (readonly, nullable) id value;
/*! The title attribute of the element. */
@property (readonly, copy) NSString *title;
/*! The label attribute of the element. */
@property (readonly, copy) NSString *label;
/*! The type of the element. /seealso XCUIElementType. */
@property (readonly) XCUIElementType elementType;
/*! Whether or not the element is enabled for user interaction. */
@property (readonly, getter = isEnabled) BOOL enabled;
/*! The horizontal size class of the element. */
@property (readonly) XCUIUserInterfaceSizeClass horizontalSizeClass;
/*! The vertical size class of the element. */
@property (readonly) XCUIUserInterfaceSizeClass verticalSizeClass;
/*! The value that is displayed when the element has no value. */
@property (readonly, nullable) NSString *placeholderValue;
/*! Whether or not the element is selected. */
@property (readonly, getter = isSelected) BOOL selected;
if TARGET_OS_TV
/*! Whether or not the elment has UI focus. */
@property (readonly) BOOL hasFocus;
endif
@end`
同时还实现了XCUIElementTypeQueryProvider协议(为指定类型的子代元素提供ready-made查询,以及一系列子元素查询.
`@protocol XCUIElementTypeQueryProvider
@property (readonly, copy) XCUIElementQuery *touchBars;
@property (readonly, copy) XCUIElementQuery *groups;
@property (readonly, copy) XCUIElementQuery *windows;
@property (readonly, copy) XCUIElementQuery *sheets;
@property (readonly, copy) XCUIElementQuery *drawers;
@property (readonly, copy) XCUIElementQuery *alerts;
@property (readonly, copy) XCUIElementQuery *dialogs;
@property (readonly, copy) XCUIElementQuery *buttons;
@property (readonly, copy) XCUIElementQuery *radioButtons;
@property (readonly, copy) XCUIElementQuery *radioGroups;
@property (readonly, copy) XCUIElementQuery *checkBoxes;
@property (readonly, copy) XCUIElementQuery *disclosureTriangles;
@property (readonly, copy) XCUIElementQuery *popUpButtons;
@property (readonly, copy) XCUIElementQuery *comboBoxes;
@property (readonly, copy) XCUIElementQuery *menuButtons;
@property (readonly, copy) XCUIElementQuery *toolbarButtons;
@property (readonly, copy) XCUIElementQuery *popovers;
@property (readonly, copy) XCUIElementQuery *keyboards;
@property (readonly, copy) XCUIElementQuery *keys;
@property (readonly, copy) XCUIElementQuery *navigationBars;
@property (readonly, copy) XCUIElementQuery *tabBars;
@property (readonly, copy) XCUIElementQuery *tabGroups;
@property (readonly, copy) XCUIElementQuery *toolbars;
@property (readonly, copy) XCUIElementQuery *statusBars;
@property (readonly, copy) XCUIElementQuery *tables;
@property (readonly, copy) XCUIElementQuery *tableRows;
@property (readonly, copy) XCUIElementQuery *tableColumns;
@property (readonly, copy) XCUIElementQuery *outlines;
@property (readonly, copy) XCUIElementQuery *outlineRows;
@property (readonly, copy) XCUIElementQuery *browsers;
@property (readonly, copy) XCUIElementQuery *collectionViews;
@property (readonly, copy) XCUIElementQuery *sliders;
@property (readonly, copy) XCUIElementQuery *pageIndicators;
@property (readonly, copy) XCUIElementQuery *progressIndicators;
@property (readonly, copy) XCUIElementQuery *activityIndicators;
@property (readonly, copy) XCUIElementQuery *segmentedControls;
@property (readonly, copy) XCUIElementQuery *pickers;
@property (readonly, copy) XCUIElementQuery *pickerWheels;
@property (readonly, copy) XCUIElementQuery *switches;
@property (readonly, copy) XCUIElementQuery *toggles;
@property (readonly, copy) XCUIElementQuery *links;
@property (readonly, copy) XCUIElementQuery *images;
@property (readonly, copy) XCUIElementQuery *icons;
@property (readonly, copy) XCUIElementQuery *searchFields;
@property (readonly, copy) XCUIElementQuery *scrollViews;
@property (readonly, copy) XCUIElementQuery *scrollBars;
@property (readonly, copy) XCUIElementQuery *staticTexts;
@property (readonly, copy) XCUIElementQuery *textFields;
@property (readonly, copy) XCUIElementQuery *secureTextFields;
@property (readonly, copy) XCUIElementQuery *datePickers;
@property (readonly, copy) XCUIElementQuery *textViews;
@property (readonly, copy) XCUIElementQuery *menus;
@property (readonly, copy) XCUIElementQuery *menuItems;
@property (readonly, copy) XCUIElementQuery *menuBars;
@property (readonly, copy) XCUIElementQuery *menuBarItems;
@property (readonly, copy) XCUIElementQuery *maps;
@property (readonly, copy) XCUIElementQuery *webViews;
@property (readonly, copy) XCUIElementQuery *steppers;
@property (readonly, copy) XCUIElementQuery *incrementArrows;
@property (readonly, copy) XCUIElementQuery *decrementArrows;
@property (readonly, copy) XCUIElementQuery *tabs;
@property (readonly, copy) XCUIElementQuery *timelines;
@property (readonly, copy) XCUIElementQuery *ratingIndicators;
@property (readonly, copy) XCUIElementQuery *valueIndicators;
@property (readonly, copy) XCUIElementQuery *splitGroups;
@property (readonly, copy) XCUIElementQuery *splitters;
@property (readonly, copy) XCUIElementQuery *relevanceIndicators;
@property (readonly, copy) XCUIElementQuery *colorWells;
@property (readonly, copy) XCUIElementQuery *helpTags;
@property (readonly, copy) XCUIElementQuery *mattes;
@property (readonly, copy) XCUIElementQuery *dockItems;
@property (readonly, copy) XCUIElementQuery *rulers;
@property (readonly, copy) XCUIElementQuery *rulerMarkers;
@property (readonly, copy) XCUIElementQuery *grids;
@property (readonly, copy) XCUIElementQuery *levelIndicators;
@property (readonly, copy) XCUIElementQuery *cells;
@property (readonly, copy) XCUIElementQuery *layoutAreas;
@property (readonly, copy) XCUIElementQuery *layoutItems;
@property (readonly, copy) XCUIElementQuery *handles;
@property (readonly, copy) XCUIElementQuery *otherElements;
@end`
③XCUIElementQuery是查询UI的类,通过类似key-value的机制得到XCUIElement的实例,使用Type(XCUIElementType枚举),Predicate,Identifier创建query,使用elementAtIndex:, elementMatchingPredicate,elementMatchingType: identifier:方法访问匹配到的UI元素,同样遵循了XCUIElementTypeQueryProvider协议.
UI测试
新建项目中一般都勾选了UITest和UnitTest选项,如果创建项目的时候忘记了勾选,新增一下即可:
新建UI测试项目初始代码:
`- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
self.continueAfterFailure = NO;
// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
[[[XCUIApplication alloc] init] launch];
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}`
UITest测试代码
`- (void)testExample {
// Use recording to get started writing UI tests.
// Use XCTAssert and related functions to verify your tests produce the correct results.
XCUIApplication *app = [[XCUIApplication alloc] init];
[app.staticTexts[@"FlyElephant"] tap];
XCUIElement *switch2 = app.switches[@"1"];
[switch2 tap];
[app.activityIndicators[@"Progress halted"] tap];
[[app.otherElements containingType:XCUIElementTypeStaticText identifier:@"FlyElephant"].element tap];
[app.progressIndicators[@"Progress"] tap];
XCUIElementQuery *steppersQuery = app.steppers;
XCUIElement *incrementButton = steppersQuery.buttons[@"Increment"];
[incrementButton tap];
XCUIElement *slider = app.sliders[@"50%"];
[slider tap];
[slider tap];
[slider tap];
XCUIElement *switch3 = app.switches[@"0"];
[switch3 tap];
[switch2 tap];
[switch3 tap];
[switch2 tap];
[switch3 tap];
[switch2 tap];
[incrementButton tap];
[steppersQuery.buttons[@"Decrement"] tap];
XCUIElement *button = app.buttons[@"Button"];
[button tap];
}`
如果想测试按钮连续疯狂点击的效果可以通过:
for (NSInteger i=0; i < 50; i++) { XCUIElement *button = app.buttons[@"Button"]; [button tap]; }
UITest 可以帮我们模拟手动无法模拟的操作,如果用的比较好,整个App的质量会有很大的提高.
参考资料:FlyElephant