44
UI Testing in Xcode 7 Dominique Stranz

UI testing in Xcode 7

Embed Size (px)

Citation preview

Page 1: UI testing in Xcode 7

UI Testing in Xcode 7Dominique Stranz

Page 2: UI testing in Xcode 7
Page 3: UI testing in Xcode 7

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

Page 4: UI testing in Xcode 7

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{

}

Page 5: UI testing in Xcode 7

RECORDING…

Page 6: UI testing in Xcode 7

UI TESTING IN XCODE 7

Recording…Problem with non-ascii characters

Not so useful with localised appsComplicated and not universal queries

Page 7: UI testing in Xcode 7

Test

abili

ty

Quality of Accessibility Data

source: WWDC, Session 406

Page 8: UI testing in Xcode 7

UI TESTING IN XCODE 7

UIAccessibility identifiers

Unique identifiers allow recorder to use simpler queries

‣ Locale independent

‣ Ignored by Voice Over

Page 9: UI testing in Xcode 7

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

Page 10: UI testing in Xcode 7

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:

Page 11: UI testing in Xcode 7

UI TESTING IN XCODE 7

Why not share our identifiers between app & test targets?

…and finally use user credentials created in setUp method.

Page 12: UI testing in Xcode 7

EXPLANATION

Page 13: UI testing in Xcode 7

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 ‣ …

Page 14: UI testing in Xcode 7

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;

Page 15: UI testing in Xcode 7

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;

Page 16: UI testing in Xcode 7

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;

Page 17: UI testing in Xcode 7

UI TESTING IN XCODE 7

Element queries

XCUIElement

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

[elementdescendantsMatchingType:XCUIElementTypeButton];

element.buttons

Page 18: UI testing in Xcode 7

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

Page 19: UI testing in Xcode 7

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

Page 20: UI testing in Xcode 7

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

Page 21: UI testing in Xcode 7

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];

Page 22: UI testing in Xcode 7

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];

Page 23: UI testing in Xcode 7

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"];

Page 24: UI testing in Xcode 7

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"];

Page 25: UI testing in Xcode 7

DEMO

Page 26: UI testing in Xcode 7

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];

Page 27: UI testing in Xcode 7

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

Page 28: UI testing in Xcode 7

TIPS & TRICKS

Page 29: UI testing in Xcode 7

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

Page 30: UI testing in Xcode 7

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

Page 31: UI testing in Xcode 7

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;}

Page 32: UI testing in Xcode 7

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

Page 33: UI testing in Xcode 7

▸ 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"]

Page 34: UI testing in Xcode 7

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:

Page 35: UI testing in Xcode 7

UI TESTING IN XCODE 7

How to speed up tests?

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

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

[UIViewsetAnimationsEnabled:NO];

}

Page 36: UI testing in Xcode 7

▸ 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];

Page 37: UI testing in Xcode 7

▸ 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];

Page 38: UI testing in Xcode 7

▸ 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"

Page 39: UI testing in Xcode 7

UI TESTING IN XCODE 7

Change device orientation

[[XCUIDevicesharedDevice]setOrientation:UIDeviceOrientationLandscapeRight];

[[XCUIDevicesharedDevice]setOrientation:UIDeviceOrientationLandscapeLeft];

▸ Landscape

[[XCUIDevicesharedDevice]setOrientation:UIDeviceOrientationPortraitFaceUp];

[[XCUIDevicesharedDevice]setOrientation:UIDeviceOrientationPortraitFaceDown];

▸ Portait

Page 40: UI testing in Xcode 7

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

Page 41: UI testing in Xcode 7

UI TESTING IN XCODE 7

Test coverage

Page 42: UI testing in Xcode 7

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?

Page 43: UI testing in Xcode 7

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)

Page 44: UI testing in Xcode 7

THANK YOU