##准备工作
对于新项目,在新建项目界面勾选上UI Tests;
对于旧项目,在项目界面点击菜单栏中的File→New→Target…→iOS→Test→iOS UITesting Bundle。
sleepForTimeInterval:
线程休眠
[NSTread sleepForTimeInterval:1.0f];
也可以使用sleep(3),OC兼容C语言。
定义测试用例
##XCTestCase
+ (void)setUp;
在类中的第一个测试方法调用之前调用,区别于-(void)setUp:在每个测试方法调用之前都调用。
+ (void)tearDown;
在类中的最后一个测试方法完成后调用。区别于-(void) tearDown:在每个测试方法调用后都调用。
###异步测试表达式
*-(XCTestExpectation *)expectationWithDescription:(NSString )description;
指定时间内满足测试条件则测试通过,超时则输出description。
- (void)testAsynExample {
XCTestExpectation *exp =[self expectationWithDescription:@"这里可以是操作出错的原因描述。。。"];
NSOperationQueue *queue =[[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
//模拟这个异步操作需要2秒后才能获取结果,比如一个异步网络请求
sleep(2);
//模拟获取的异步操作后,获取结果,判断异步方法的结果是否正确
XCTAssertEqual(@"a",@"a");
//如果断言没问题,就调用fulfill宣布测试满足
[exp fulfill];
}];
//设置延迟多少秒后,如果没有满足测试条件就报错
[self waitForExpectationsWithTimeout:3 handler:^(NSError * _Nullable error){
if(error){
NSLog(
@"Timeout Error: %@", error);
}
}];
}
*-(XCTestExpectation *)expectationForPredicate:(NSPredicate )predicate evaluatedWithObject:(id)object handler:(XCPredicateExpectationHandler)handler;
利用谓词计算,如果限定时间内满足条件则通过测试
- (void)testThatBackgroundImageChanges {
XCTAssertNil([self.button backgroundImageForState:UIControlStateNormal]);
NSPredicate *predicate =[NSPredicate predicateWithBlock:^BOOL(UIButton * _Nonnull button,NSDictionary<NSString *,id>* _Nullable bindings){
return[button backgroundImageForState:UIControlStateNormal]!=nil;
}];
[self expectationForPredicate:predicate evaluatedWithObject:self.button handler:nil];
[self waitForExpectationsWithTimeout:20 handler:nil];
}
*-(XCTestExpectation *)expectationForNotification:(NSString )notificationName object:(id)objectToObserve handler:(XCNotificationExpectationHandler)handler;
监听一个通知,如果在规定时间内正确收到通知则测试通过。
- (void)testAsynExample1 {
[self expectationForNotification:(@"监听通知的名称xxx") object:nil handler:nil];
[[NSNotificationCenter defaultCenter]postNotificationName:@"监听通知的名称xxx" object:nil];
//设置延迟多少秒后,如果没有满足测试条件就报错
[self waitForExpectationsWithTimeout:3 handler:nil];
}
*- (XCTestExpectation *)keyValueObservingExpectationForObject:(id)objectToObserve keyPath:(NSString )keyPath expectedValue:(id)expectedValue;
创建一个KVO观察模式
- (XCTestExpectation *)keyValueObservingExpectationForObject:(id)objectToObserve keyPath:(NSString *)keyPath handler:(XCKeyValueObservingExpectationHandler)handler;
创建一个KVO观察模式
-(void)waitForExpectationsWithTimeout:(NSTimeInterval)timeout handler:(XCWaitCompletionHandler)handler;
设定等待时间,等待时间内满足所有条件则测试通过,成功或超时都会执行handler block(optional)
typedef BOOL (^XCPredicateExpectationHandler)(void);
如果未提供Handle,第一次测试通过即满足条件,如果提供了Handle,它能覆盖原有的行为和条件,那么将重新判定是否满足条件。
typedef BOOL(^XCNotificationExpectationHandler)(NSNotification *notification);
获得符合期望的通知时将被调用,满足条件为Yes
typedef BOOL (^XCKeyValueObservingExpectationHandler)(id observedObject, NSDictionary *change);
当KVO监视的值反正改变是调用,满足条件为Yes
typedef void(^XCWaitCompletionHandler)(NSError *error);
当测试成功或超时时调用,需要指定error类型,否则error = nil;
@property BOOL continueAfterFailure;
默认为Yes,当case中某条测试语句失败时会继续向下执行,实测只向下执行了一步,待验证。
- (void)measureBlock:(void(^)(void))block;
测试块中代码的性能。
- (void)measureMetrics:(NSArray<NSString >)metrics automaticallyStartMeasuring:(BOOL)automaticallyStartMeasuring forBlock:(void(^)(void))block;
measureBlock的拓展版,当需要自定义测量的开始点和结束点时,又或者要测量多个指标时调用此方法。
Metrics:是测量标准数组;automaticallyStartMeasuring为真时,自动开始测试,为假则需要startMeasuring作为启动点。
注意在一个代码块中开始点和结束点只能各有一个,出现一下情况时测试将会失败: automaticallyStartMeasuring = YES且代码块中调用了startMeasuring方法; automaticalltStattMeasuring = NO 且代码块中没调用或多次调用了startMeasuring方法;
在代码块中多次调用了stopMeasuring方法。
-(void)startMeasuring;
在measureBlock中调用此方法来标记一个测量起点。
-(void)stopMeasuring;
在measureBlock中调用此方法来标记一个结束点。
+(NSArray<NSString >)defaultPerformanceMetrics;
这是调用measureBlock时默认使用的测量标准数组。
-(id
*interruptingElement))handler;
在当前上下文中添加一个Handle
handlerDescription:用于阐述这个Handle的作用和行为,主要被用来Debug和分析异步测试
XCTestExpectation
使用以下XCTestCase方法来创建XCTestExpectation实例: expectationWithDescription:
expectationForPredicate:evaluatedWithObject:handler: expectationForNotification:object:handler: keyValueObservingExpectationForObject:keyPath:expectedValue: keyValueObservingExpectationForObject:keyPath:handler:
-(void)fulfill;
为满足条件的表达式做标记
布尔值检测
XCTAssert / XCTAssertTrue
断言表达式为真,XCTAssert(expression, format...)当expression求值为TRUE时通过; XCTAssert([image exists]);
XCTAssertTrue(expression, format...)当expression求值为TRUE时通过; XCTAssertTure([image exists]);
XCTAssertFalse
表达式为假,XCTAssertFalse(expression, format...)当expression求值为False时通过; XCTAssertFalse(![image exists]);
空值检测
XCTAssertNil
表达式的值为空,XCTAssertNil(a1, format...)为空判断,a1为空时通过,反之不通过; NSArray *array =nil;
XCTAssertNil(array);
XCTAssertNotNil
表达式的值非空,XCTAssertNotNil(a1, format…)不为空判断,a1不为空时通过,反之不通过; NSArray *array =[NSArray array];
XCTAssertNotNil(array);
等式检测
XCTAssertEqual
XCTAssertEqual(a1, a2, format...)判断相等(当a1和a2是C语言标量、结构体或联合体时使用, 判断的是变量的地址,如果地址相同则返回TRUE,否则返回NO);
XCTAssertEqual(array,array2,@"失败时输出");
XCTAssertEqualObjects
XCTAssertEqualObjects(a1, a2, format...)判断相等,[a1 isEqual:a2]值为TRUE时通过,其中一个不为空时,不通过;
XCTAssertEqualObjects(array,array2,@"失败时输出"); XCTAssertEqualWithAccuracy
XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)判断相等,(double或float类型)提供一个误差范围,当在误差范围(+/-accuracy)以内相等时通过测试; XCTAssertEquallWithAccuracy(array,array2,@"失败时输出");
不等式检测
XCTAssertNotEqual
XCTAssertNotEqual(a1, a2, format...)判断不等(当a1和a2是C语言标量、结构体或联合体时使用);
XCTAssertNotEqual(array,array2,@"失败时输出"); XCTAssertNotEqualObjects
XCTAssertNotEqualObjects(a1, a2, format...)判断不等,[a1 isEqual:a2]值为False时通过; XCTAssertNotEqualObjects(array,array2,@"失败时输出"); XCTAssertNotEqualWithAccuracy
XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...) 判断不等,(double或float类型)提供一个误差范围,当在误差范围以内不等时通过测试; XCTAssertNotEquallWithAccuracy(array,array2,@"失败时输出");
相对值检测
XCTAssertGreaterThan:A > B
XCTAssertGreaterThan(floatB,floatA,@"Fail Output"); XCTAssertGreaterThanOrEqual:A ≥ B
XCTAssertGreaterThanOrEqual(floatB,floatA,@"Fail Output"); XCTAssertLessThan:A< B
XCTAssertLessThan(floatB,floatA,@"Fail Output"); XCTAssertLessThanOrEqual:A ≤ B
XCTAssertLessThanOrEqual(floatB,floatA,@"Fail Output");
异常检测
XCTAssertThrows(expression, format...)
异常测试,当expression发生异常时通过;反之不通过;
XCTAssertThrowsSpecific(expression, specificException, format...)
异常测试,当expression发生specificException异常时通过;反之发生其他异常或不发生异常均不通过;
XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)
异常测试,当expression发生具体异常、具体异常名称的异常时通过测试,反之不通过; XCTAssertNoThrow(expression, format…)
异常测试,当expression没有发生异常时通过测试;
XCTAssertNoThrowSpecific(expression, specificException, format...)
异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过; XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...) 异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过。无条件失败断言
XCTFail
无条件产生一个失败的结果。
XCTFail();
UI Testing
XCUIElements API
exists
判断控件对象是否存在。BOOL类型。
[textField exists]
debugDescription
保存某控件的debug信息,这些信息只能用于调试case,NSString类型。
NSLog(@"%@",[textField debugDescription]);
hittable
BOOL类型的只读属性,表示当前元素能否获取到坐标。
descendantsMatchingType
从该控件下所有子控件中找到符合指定类型的控件,需要传入XCUIElementType(枚举类,定义了iOS中所有可定位的控件)类型的参数,返回包含了XCUIElementType类型的XCUIElementQuery数组。
XCUIElementQuery *textFields =[cell
childrenMatchingType:XCUIElementTypeTextField];
childrenMatchingType
只从与该控件有直接关系的子控件中找到符合指定类型的控件,需要传入XCUIElementType 类型的参数,返回包含了XCUIElementType类型的XCUIElementQuery数组。XCUIElementQuery *textFields =[cell
descendantsMatchingType:XCUIElementTypeTextField];
tap
单击
[app.tables.staticTexts[@"Groceries"]tap];
。doubleTap
双击
[buttondoubletap];
twoFingerTap
双指单击
[app twoFingerTap];
pressForDuration(duration: NSTimeInterval)
长按,时间由传入的参数定义,单位为秒
[textField pressForDuration:5.5];
pressForDuration(duration: NSTimeInterval, thenDragToElement otherElement: XCUIElement) 长按拖拽。在控件上长按后,拖拽到另外一个控件。传入2个参数:长按时间和拖拽到目标控件。
[textField pressForDuration:5.5, thenDragToElement:table];
swipeUp/ swipeDown/ swipeLeft/ swipeRight
从下划到上/从上滑到下/从右滑到左/从左滑到右
[app swipeUp];
typeText
输入字符。需要一个参数:NSString
[addItemTextField typeText:@"Hello"];
tapWithNumberOfTaps:numberOfTouches:
多触摸点及多次点击
[windows tapWithNumberOfTaps:3 numberOfTouches:2];
pinchWithScale:velocity:
捏合手势scale=0~1为捏合、>1为放大,velocity为捏合速度
[windows pinchWithScale:0.2 velocity:-0.05];
当0<scale<1时,velocity必须小于0;当scale>1时,velocity必须大于0,time(s) = scale/velocity。
[img pinchWithScale:0.5 velocity:0.2];
rotate:withVelocity:
旋转手势rotate:要旋转的弧度withVelocity:每秒要旋转的弧度
Rotate和Velocity必须同号顺时针为正向。
[img rotate:2 withVelocity:0.4];
normalizedSliderPosition
只读属性,返回滑块控件中滑块的位置(0~1) adjustToNormalizedSliderPosition:
尽可能让滑块移动到指定的位置(0-1)
adjustToPickerWheelValue:
输入字符串让选择器显示对应内容,如果没有对应内容,返回Fail coordinateWithNormalizedOffset:
根据控件的原点坐标和偏移量来确定一个新坐标
[element coordinateWithNormalizedOffset:CGVectorMake(10,10)]; XCUIApplication API
XCTest新加的类,用于做UI测试,代表被测应用,父类为XCUIElement
launch
启动应用。如果目标应用已运行,首先终止应用,然后再次启动应用。[applaunch];
terminate
关闭应用。
[app terminate];
launchArguments
数组对象,保存启动参数。
NSArray*args=[applaunchArguments];
for(int i=0;i<[argscount];i++){
NSLog(@"arg : %@",[argsobjectAtIndex:i]);
}
launchEnvironment
字典对象,保存启动环境变量
NSDictionary *env =[app launchEnvironment];
for(id key in env){
NSString *object=[env objectForKey:key];
NSLog(@"env : %@",object);
}
XCUIElementAttributesAPI
协议类,XCUIElement遵守的协议
identifier
字符串类型Accessibility ID
NSString *identifier =[app identifier];
.frame
控件的矩形区域
CGRect frame =[app frame];
Value
获取元素的原值
id value =[app value];
placeholderValue
返回元素的占位值
title
标题,String类型
NSString *title =[app title];
label
标签值,String类型
NSString *label =[app label];
elementType
控件类型
XCUIElementType *elementType =[app elementType];
enabled
是否可用,BOOL类型
BOOL*isEnabled =[app isEnabled];
hasFocus
是否具有UI焦点
Selected
是否处于被选中状态
horizontalSizeClass
返回水平尺寸元素
XCUIUserInterfaceSizeClass *horizontalSizeClass =[app horizontalSizeClass];
verticalSizeClass
返回垂直尺寸元素
XCUIUserInterfaceSizeClass *verticalSizeClass =[app verticalSizeClass];
XCUIElementQueryAPI
定位元素的对象,可以理解为存放控件的容器
element
query用element表示形式,如果query中只有一个元素,可以讲element当成真正的element,执行点击等操作,从这一方面来讲XCUIElementQuery其实也是一种XCUIElement对象,只是是用来存放0~N个XCUIElement的容器。得到XCUIElement对象。
count
query中找到的元素数量,得到整数。
allElementsBoundByAccessibilityElement
query中根据accessibility element得到的元素数组。得到XCUIElement数组
allElementsBoundByIndex
query中根据索引值得到的元素数组。得到XCUIElement数组
debugDescription
调试信息
- 。:
获得传入的索引值所在的元素,返回XCUIElement对象。
elementMatchingPredicate
根据NSPredicate定义的匹配条件查找元素。返回XCUIElement对象。只能从当前对象中查找。更深层次的元素不在查找范围内
elementMatchingType
根据元素类型(XCUIElementType)和id号来匹配查找元素。返回XCUIElement对象。只能从当前对象中查找。更深层次的元素不在查找范围内
descendantsMatchingType
传入XCUIElementType作为匹配条件,得到匹配的XCUIElementQuery对象,查找对象为当前控件的子子孙孙控件。返回XCUIElementQuery对象
childrenMatchingType
传入XCUIElementType作为匹配条件,得到匹配的XCUIElementQuery对象,查找对象为当前控件的子控件。返回XCUIElementQuery对象
matchingPredicate
传入NSPredicate作为过滤器,得到XCUIElementQuery对象。返回XCUIElementQuery对象
matchingType
传入XCUIElementType和id号作为匹配条件,得到XCUIElementQuery。返回XCUIElementQuery对象
matchingIdentifier
传入id号作为匹配条件,得到XCUIElementQuery。返回XCUIElementQuery对象
containingPredicate
传入NSPredicate过滤器作为匹配条件。从子节点中找到包含该条件的XCUIElementQuery 对象
containingType
传入XCUIElementType和id作为匹配条件。从子节点中找到包含该条件的XCUIElementQuery对象。
XCUIElementTypeAPI & XCUIElementTypeQueryProvider API 枚举类,定义了iOS中所有的可用于搜索类型
XCUIElementTypeQueryProvider协议中定义了76个变量,与XCUIElementType定义的枚举元素相比少了3个:Any,Unknown,Application。因为XCUIApplication也遵循该协议,所以Application对象包含XCUIElementTypeQueryProvider定义的所有属性,所以要过滤掉以上三个大于Application的类型。
除了特殊注明的,XCUIElementQuery都是原来类型的复数形式
UIView的定位方式:app.otherElements[@”id”];