iOS-UITest测试

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 的信息.

iOS-UITest测试_第1张图片
FlyElephant.png

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选项,如果创建项目的时候忘记了勾选,新增一下即可:


iOS-UITest测试_第2张图片
FlyElephant.png

新建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

你可能感兴趣的:(iOS-UITest测试)