ui testing in xcode 7

Post on 11-Apr-2017

25 Views

Category:

Mobile

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

UI Testing in Xcode 7Dominique Stranz

UI TESTING IN XCODE 7

Example Test: Successful user login

1) tap my account button

2) tap log in button

3) tap & type e-mail

4) tap & type password

5) tap log in button

UI TESTING IN XCODE 7

Example Test: Successful user login

@interfaceAccount:XCTestCase

@property(strong)TestUser*user;

@end

@implementationAccount

-(void)setUp{

[supersetUp];

[selfcreateUser];//wearecreatingtestuseraccounthere,itwillbeusedinalltestcases

self.continueAfterFailure=NO;}

-(void)tearDown{[supertearDown];}

-(void)testLogin_CredentialsAreValid_ShouldLogin{

}

RECORDING…

UI TESTING IN XCODE 7

Recording…Problem with non-ascii characters

Not so useful with localised appsComplicated and not universal queries

Test

abili

ty

Quality of Accessibility Data

source: WWDC, Session 406

UI TESTING IN XCODE 7

UIAccessibility identifiers

Unique identifiers allow recorder to use simpler queries

‣ Locale independent

‣ Ignored by Voice Over

UI TESTING IN XCODE 7

How to set UIAccessibility identifiers?

@protocolUIAccessibilityIdentification<NSObject>

@required

/*

Astringthatidentifiestheuserinterfaceelement.

default==nil

*/

@property(nullable,nonatomic,copy)NSString*accessibilityIdentifierNS_AVAILABLE_IOS(5_0);

@end

UI TESTING IN XCODE 7

We can choose from several options to find element

We should use more precise query, if our accessibility identifier or label isn’t unique:

UI TESTING IN XCODE 7

Why not share our identifiers between app & test targets?

…and finally use user credentials created in setUp method.

EXPLANATION

UI TESTING IN XCODE 7

XCUIElement - proxy for all application UI elements

XCUIElement UIButton

UITextField

UITableViewCell

and others…

‣ currentTitle ‣ currentAttributedTitle

‣ text ‣ placeholder

‣ textLabel.text ‣ detailTextLabel.text

‣ title ‣ label ‣ value ‣ placeholderValue ‣ identifier ‣ selected ‣ frame ‣ …

UI TESTING IN XCODE 7

Interactions

XCUIElement

‣ title ‣ label ‣ value ‣ placeholderValue ‣ identifier ‣ selected ‣ frame ‣ …

-(void)typeText:(NSString*)text;

-(void)tap;

-(void)doubleTap;

-(void)twoFingerTap;

-(void)tapWithNumberOfTaps:(NSUInteger)numberOfTapsnumberOfTouches:(NSUInteger)numberOfTouches;

-(void)pressForDuration:(NSTimeInterval)duration;

-(void)pressForDuration:(NSTimeInterval)durationthenDragToElement:(XCUIElement*)otherElement;

-(void)swipeUp;

-(void)swipeDown;

-(void)swipeLeft;

-(void)swipeRight;

-(void)pinchWithScale:(CGFloat)scalevelocity:(CGFloat)velocity;

-(void)rotate:(CGFloat)rotationwithVelocity:(CGFloat)velocity;

UI TESTING IN XCODE 7

Element queries

XCUIElement

‣ title ‣ label ‣ value ‣ placeholderValue ‣ identifier ‣ selected ‣ frame ‣ …

/*!Returnsaqueryforalldescendantsoftheelementmatchingthespecifiedtype.*/

-(XCUIElementQuery*)descendantsMatchingType:(XCUIElementType)type;

/*!Returnsaqueryfordirectchildrenoftheelementmatchingthespecifiedtype.*/

-(XCUIElementQuery*)childrenMatchingType:(XCUIElementType)type;

UI TESTING IN XCODE 7

Element queries

XCUIElement

‣ title ‣ label ‣ value ‣ placeholderValue ‣ identifier ‣ selected ‣ frame ‣ …

/*!Returnsaqueryforalldescendantsoftheelementmatchingthespecifiedtype.*/

-(XCUIElementQuery*)descendantsMatchingType:(XCUIElementType)type;

@property(readonly,copy)XCUIElementQuery*tables;

@property(readonly,copy)XCUIElementQuery*buttons;

@property(readonly,copy)XCUIElementQuery*staticTexts;

@property(readonly,copy)XCUIElementQuery*textFields;

@property(readonly,copy)XCUIElementQuery*secureTextFields;

@property(readonly,copy)XCUIElementQuery*textViews;

UI TESTING IN XCODE 7

Element queries

XCUIElement

‣ title ‣ label ‣ value ‣ placeholderValue ‣ identifier ‣ selected ‣ frame ‣ …

[elementdescendantsMatchingType:XCUIElementTypeButton];

element.buttons

UI TESTING IN XCODE 7

Root element - XCUIApplication

@interfaceXCUIApplication:XCUIElement

-(void)launch;

-(void)terminate;

@property(nonatomic,copy)NSArray<NSString*>*launchArguments;

@property(nonatomic,copy)NSDictionary<NSString*,NSString*>*launchEnvironment;

@end

UI TESTING IN XCODE 7

XCUIElementQuery

@interfaceXCUIElementQuery:NSObject<XCUIElementTypeQueryProvider>

@property(readonly)NSUIntegercount;

-(XCUIElement*)elementBoundByIndex:(NSUInteger)index;

-(XCUIElement*)elementMatchingPredicate:(NSPredicate*)predicate;

-(XCUIElement*)elementMatchingType:(XCUIElementType)elementTypeidentifier:(nullableNSString*)identifier;

-(XCUIElement*)objectForKeyedSubscript:(NSString*)key;

@end

UI TESTING IN XCODE 7

XCUIElementQuery

@interfaceXCUIElementQuery:NSObject<XCUIElementTypeQueryProvider>

@property(readonly)NSUIntegercount;

-(XCUIElement*)elementBoundByIndex:(NSUInteger)index;

-(XCUIElement*)elementMatchingPredicate:(NSPredicate*)predicate;

-(XCUIElement*)elementMatchingType:(XCUIElementType)elementTypeidentifier:(nullableNSString*)identifier;

-(XCUIElement*)objectForKeyedSubscript:(NSString*)key;

@end

▸ Evaluates the query

▸ Return the number of matches found

UI TESTING IN XCODE 7

XCUIElementQuery

@interfaceXCUIElementQuery:NSObject<XCUIElementTypeQueryProvider>

@property(readonly)NSUIntegercount;

-(XCUIElement*)elementBoundByIndex:(NSUInteger)index;

-(XCUIElement*)elementMatchingPredicate:(NSPredicate*)predicate;

-(XCUIElement*)elementMatchingType:(XCUIElementType)elementTypeidentifier:(nullableNSString*)identifier;

-(XCUIElement*)objectForKeyedSubscript:(NSString*)key;

@end

▸ Return nth element in query results

▸ Example:XCUIElement*firstCell=[app.tables.cellselementBoundByIndex:0];

UI TESTING IN XCODE 7

XCUIElementQuery

@interfaceXCUIElementQuery:NSObject<XCUIElementTypeQueryProvider>

@property(readonly)NSUIntegercount;

-(XCUIElement*)elementBoundByIndex:(NSUInteger)index;

-(XCUIElement*)elementMatchingPredicate:(NSPredicate*)predicate;

-(XCUIElement*)elementMatchingType:(XCUIElementType)elementTypeidentifier:(nullableNSString*)identifier;

-(XCUIElement*)objectForKeyedSubscript:(NSString*)key;

@end

▸ Example:NSPredicate*labelPrefixPredicate=[NSPredicatepredicateWithFormat:

@"labelBEGINSWITH%@",prefix];

XCUIElement*label=[app.staticTextselementMatchingPredicate:labelPrefixPredicate];

UI TESTING IN XCODE 7

XCUIElementQuery

@interfaceXCUIElementQuery:NSObject<XCUIElementTypeQueryProvider>

@property(readonly)NSUIntegercount;

-(XCUIElement*)elementBoundByIndex:(NSUInteger)index;

-(XCUIElement*)elementMatchingPredicate:(NSPredicate*)predicate;

-(XCUIElement*)elementMatchingType:(XCUIElementType)elementTypeidentifier:(nullableNSString*)identifier;

-(XCUIElement*)objectForKeyedSubscript:(NSString*)key;

@end

▸ Example:XCUIElement*button=[appelementMatchingType:XCUIElementTypeButtonidentifier:@"login"];

UI TESTING IN XCODE 7

XCUIElementQuery

@interfaceXCUIElementQuery:NSObject<XCUIElementTypeQueryProvider>

@property(readonly)NSUIntegercount;

-(XCUIElement*)elementBoundByIndex:(NSUInteger)index;

-(XCUIElement*)elementMatchingPredicate:(NSPredicate*)predicate;

-(XCUIElement*)elementMatchingType:(XCUIElementType)elementTypeidentifier:(nullableNSString*)identifier;

-(XCUIElement*)objectForKeyedSubscript:(NSString*)key;

@end▸ Example:XCUIElement*button=app.buttons[@"login"];

DEMO

UI TESTING IN XCODE 7

How to asset test results?

▸ We can use all variants of XCTAssert macro

▸ But more accurate for asynchronous UI are expectations

XCUIElement*loggedInLabel=app.tables.staticTexts[Identifier_Account_LoggedInLabel];NSPredicate*exists=[NSPredicatepredicateWithFormat:@"exists==1"];

[selfexpectationForPredicate:existsevaluatedWithObject:loggedInLabelhandler:nil];

[selfwaitForExpectationsWithTimeout:15handler:nil];

UI TESTING IN XCODE 7

Performance test

-(void)testLogin_CredentialsAreValid_ShouldLogin{[selfmeasureBlock:^{

//UITestcode}];

}

▸ Xcode executes each test 10 times and compare average execution time with baseline (selected from previous results)

▸ Test will fail if new average has increased from baseline by 10% or more, but it will ignore regressions of less than a tenth of a second

TIPS & TRICKS

UI TESTING IN XCODE 7

Handle UI interruptions

▸ Setup interruptions monitor[selfaddUIInterruptionMonitorWithDescription:@"LocationPermission"

handler:^BOOL(XCUIElement*element){XCUIElement*button=alert.buttons[@"Allow"];if(button.exists){[buttontap];returntrue;}returnfalse;}];

▸ Handlers are invoked in reverse order until one of them return true

1 2

UI TESTING IN XCODE 7

Handle UI interruptions

▸ By default, XCode 7.2 will try to find & tap elements matching predicate:

userTestingAttributesCONTAINS"cancel-button"

userTestingAttributesCONTAINS"default-button"

1 2

UI TESTING IN XCODE 7

Handle UI interruptions

▸ By default, XCode 7.2 will try to find & tap elements matching predicate:

userTestingAttributesCONTAINS"cancel-button"

userTestingAttributesCONTAINS"default-button"

2▸ Do you prefer confirm button as first choice? Add your own monitor:

NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"userTestingAttributesCONTAINS\"default-button\""];

XCUIElement*button=[alert.buttonselementMatchingPredicate:predicate];if(button.exists){[buttontap];returntrue;}

UI TESTING IN XCODE 7

Typing in secure text field doesn’t work

▸ Workaround: Disconnect Hardware Keyboard in Simulator

▸ „Neither element or any descendant has keyboard focus” error occurs when you are trying to type in secure UITextField

▸ Instead, we should check if element is hittable:

UI TESTING IN XCODE 7

Why your test is not waiting for hidden elements?

▸ Hidden elements fulfil exists predicate:[NSPredicatepredicateWithFormat:@"exists==1"]

[NSPredicatepredicateWithFormat:@"hittable==1"]

UI TESTING IN XCODE 7

How to detect that app is in test mode?

XCUIApplication*app=[[XCUIApplicationalloc]init];

[appsetLaunchArguments:@[@"UITESTS"]];

[applaunch];

NSArray*arguments=[[NSProcessInfoprocessInfo]arguments];

if([argumentscontainsObject:@"UITESTS"]){

//Customizeappfortests

}

▸ In test file:

▸ In application:

UI TESTING IN XCODE 7

How to speed up tests?

▸ Disable animations in AppDelegate:NSArray*arguments=[[NSProcessInfoprocessInfo]arguments];

if([argumentscontainsObject:@"UITESTS"]){

[UIViewsetAnimationsEnabled:NO];

}

▸ Waiting for element is one of the most cases in UI tests, why not use convenient helper?

UI TESTING IN XCODE 7

Waiting for elements shortcuts

NSPredicate*exists=[NSPredicatepredicateWithFormat:@"exists==1"];[selfexpectationForPredicate:existsevaluatedWithObject:elementhandler:nil];[selfwaitForExpectationsWithTimeout:15handler:nil];

[selfwaitForElement:element];

[selfwaitForElement:buttonwithTimeout:60];

▸ Waiting for element is one of the most cases in UI tests, why not use convenient helper?

UI TESTING IN XCODE 7

Waiting for elements shortcuts

NSPredicate*exists=[NSPredicatepredicateWithFormat:@"hittable==1"];[selfexpectationForPredicate:existsevaluatedWithObject:elementhandler:nil];[selfwaitForExpectationsWithTimeout:15handler:nil];

[selfwaitForElementHittable:element];

[selfwaitForElementHittable:buttonwithTimeout:60];

▸ Waiting for element is one of the most cases in UI tests, why not use convenient helper?

UI TESTING IN XCODE 7

Waiting for elements shortcuts

NSPredicate*exists=[NSPredicatepredicateWithFormat:@"hittable==1"];[selfexpectationForPredicate:existsevaluatedWithObject:elementhandler:nil];[selfwaitForExpectationsWithTimeout:15handler:nil];

[selfwaitForElementHittable:element];

[selfwaitForElementHittable:buttonwithTimeout:60];

https://github.com/dstranz/XCUITestsAdditionspod"XCUITestsAdditions"

UI TESTING IN XCODE 7

Change device orientation

[[XCUIDevicesharedDevice]setOrientation:UIDeviceOrientationLandscapeRight];

[[XCUIDevicesharedDevice]setOrientation:UIDeviceOrientationLandscapeLeft];

▸ Landscape

[[XCUIDevicesharedDevice]setOrientation:UIDeviceOrientationPortraitFaceUp];

[[XCUIDevicesharedDevice]setOrientation:UIDeviceOrientationPortraitFaceDown];

▸ Portait

UI TESTING IN XCODE 7

Simulates the user pressing a physical button

[[XCUIDevicesharedDevice]pressButton:XCUIDeviceButtonHome];

[[XCUIDevicesharedDevice]pressButton:XCUIDeviceButtonVolumeUp];

[[XCUIDevicesharedDevice]pressButton:XCUIDeviceButtonVolumeDown];

▸ Home button

▸ Volume up

▸ Volume down

▸ Volume up & down doesn’t work on simulator

UI TESTING IN XCODE 7

Test coverage

UI TESTING IN XCODE 7

How to reset simulator state before each test?

It’s not possible to force launching app on clean simulator (rdar://22455111).

What are workarounds?

UI TESTING IN XCODE 7

How to reset simulator state before each test?

▸ If you are using CI, you can run xcrun simctl erase all between each test cases

▸ You can clean NSUserDefaults and Keychain in AppDelegate, when app is in UITests mode

▸ …or rollback changes in tearDown method (for example logout user after login test)

THANK YOU

top related