XCTest简介

2018/9/13 posted in  Xcode

准备工作

对于新项目,在新建项目界面勾选上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*)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*)defaultPerformanceMetrics;

这是调用measureBlock时默认使用的测量标准数组。

-(id)addUIInterruptionMonitorWithDescription:(NSString *)handlerDescription handler:(BOOL()(XCUIElement

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

当01时,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”];