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

FlyElephant.png

UITest核心的类有三个XCUIApplication,XCUIElement 和XCUIElementQuery.

①XCUIApplication是Application的代理,用来启动或者是终止UI测试程序.启动参数设置,在获取程序中的UI元素。

②XCUIElement是XCTest.framework对应用中的所有UI控件的抽象,所有的UI控件都是此类型,滑动,点击,轻扫手势:
<pre><code>`@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`</code></pre>

XCUIElementAttributes协议描述UI元素的属性:Identity,Value,Interaction State,Size,elementType等属性.
<pre><code>`@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`</code></pre>

同时还实现了XCUIElementTypeQueryProvider协议(为指定类型的子代元素提供ready-made查询,以及一系列子元素查询.
<pre><code>`@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`</code></pre>

③XCUIElementQuery是查询UI的类,通过类似key-value的机制得到XCUIElement的实例,使用Type(XCUIElementType枚举),Predicate,Identifier创建query,使用elementAtIndex:, elementMatchingPredicate,elementMatchingType: identifier:方法访问匹配到的UI元素,同样遵循了XCUIElementTypeQueryProvider协议.

UI测试

新建项目中一般都勾选了UITest和UnitTest选项,如果创建项目的时候忘记了勾选,新增一下即可:


FlyElephant.png

新建UI测试项目初始代码:
<pre><code>`- (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];
    }`</code></pre>

UITest测试代码
<pre><code>`- (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];

}`</code></pre>

如果想测试按钮连续疯狂点击的效果可以通过:
<pre><code>for (NSInteger i=0; i < 50; i++) { XCUIElement *button = app.buttons[@"Button"]; [button tap]; }</code></pre>
UITest 可以帮我们模拟手动无法模拟的操作,如果用的比较好,整个App的质量会有很大的提高.
参考资料:FlyElephant

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容