iOS-UITest測試

iOS4時代,UIAutomation是可以在真實設備和iPhone模擬器上執(zhí)行自動化測試的常用框架.UIAutomation的功能測試代碼是用Javascript編寫的。UIAutomation和Accessibility有著直接的關系,通過標簽和值的訪問性來獲得UI元素.

UITest是Xcode7.0之后蘋果推出的,主要功能也是頁面UI測試,相比之前而言,可以通過OC或者Swift更熟悉的語言編寫UI測試代碼,UIAutomatio已經不被蘋果棄用,UITest最大的亮點是支持屏幕錄制——通過對App的操作自動生成相應的測試腳本代碼.即時我們不懂開發(fā)也可以很快寫出基本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創(chuàng)建query,使用elementAtIndex:, elementMatchingPredicate,elementMatchingType: identifier:方法訪問匹配到的UI元素,同樣遵循了XCUIElementTypeQueryProvider協議.

UI測試

新建項目中一般都勾選了UITest和UnitTest選項,如果創(chuàng)建項目的時候忘記了勾選,新增一下即可:


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>

如果想測試按鈕連續(xù)瘋狂點擊的效果可以通過:
<pre><code>for (NSInteger i=0; i < 50; i++) { XCUIElement *button = app.buttons[@"Button"]; [button tap]; }</code></pre>
UITest 可以幫我們模擬手動無法模擬的操作,如果用的比較好,整個App的質量會有很大的提高.
參考資料:FlyElephant

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,366評論 25 708
  • 0.小目標 關于UI自動化的定義,我想要的是自動地按照流程去點擊頁面、輸入數據,不需要人去參與,節(jié)省人工時間。比如...
    孢子菌閱讀 15,854評論 10 47
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發(fā)現,斷路器,智...
    卡卡羅2017閱讀 136,715評論 19 139
  • 2017-10.24 從我接觸減肥開始到現在,被問到很多的一個問題,就是吃什么能減肥?其實仔細想想,這個問...
    xyz心若幽蘭閱讀 320評論 0 1
  • 第一次去肯德基,是在小學。 十年前,肯德基對小孩子的誘惑,堪比一場豪華的貪吃盛宴。媽媽會在考試之前跟我講:考到班級...
    le小白閱讀 772評論 0 0

友情鏈接更多精彩內容