前言
单元测试和UI测试大致步骤网上很多文章都有,如果会的可以忽略,关键是错误总结,网上很少有文章提及到,感兴趣的读者可以拉到最后面看看总结,相信你一定有所收获!!!
unitTests测试
1.unitTests作用
由于只是一些简单实用的东西,学学还是挺不错的。其实单元测试用的好,开发起来也会快很多。单元测试对于我目前来说,就是为了方便测试一些功能是否正常运行,还有调试接口是否能正常使用。有时候你可能是为了测试某一个网络接口,然后每次都重新启动并且经过很多操作之后才测试到了那个网络接口。如果使用了单元测试,就可以直接测试那个方法,相对方便很多。 比如由于修改较多,我们想测试一下分享功能是否正常,这时候就有用了。(而不是重新启动程序,进入到分享界面,点击分享,填写分享内容。)其实单元测试并没有降低我们打代码的效率,我们可以在单元测试通过了,直接用到相应的地方。
当然也有一些高级的作用,比如自动发布、自动测试(特别在一些大的项目,以防止程序被误改或引起新的问题)。
现在,让我们先理清一下单元测试到底有些什么东西?
OCUnit(即用XCTest进行测试)其实就是苹果自带的测试框架,我们主要讲的就是这个。GHUnit是一个可视化的测试框架。(有了它,你可以点击APP来决定测试哪个方法,并且可以点击查看测试结果等。)OCMock就是模拟某个方法或者属性的返回值,你可能会疑惑为什么要这样做?使用用模型生成的模型对象,再传进去不就可以了?答案是可以的,但是有特殊的情况。比如你测试的是方法A,方法A里面调用到了方法B,而且方法B是有参数传入,但又不是方法A所提供。这时候,你可以使用OCMock来模拟方法B返回的值。(在不影响测试的情况下,就可以这样去模拟。)除了这些,在没有网络的情况下,也可以通过OCMock模拟返回的数据。UITests就是通过代码化来实现自动点击界面,输入文字等功能。靠人工操作的方式来覆盖所有测试用例是非常困难的,尤其是加入新功能以后,旧的功能也要重新测试一遍,这导致了测试需要花非常多的时间来进行回归测试,这里产生了大量重复的工作,而这些重复的工作有些是可以自动完成的,这时候UITests就可以帮助解决这个问题了。
2.unitTests的使用
创建一个工程,名字随便取,直接勾选include Unit Tests
万一我忘了勾选怎么办呢?可以有其他方式创建File-->new-->target-->iOS-->iOS Unit Testing Bundle。名字自己看着办吧。
工程创建好后,那要怎么开始测试呢?
找到系统单元测试Testes文件夹中.m文件看中会到看到几个方法,我们来看下这个几个方法是什么时候调用和他们各种的作用
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
//初始化的代码,在测试方法调用之前调用
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
// 释放测试用例的资源代码,这个方法会每个测试用例执行后调用
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// 测试用例的例子,注意测试用例一定要test开头
}
- (void)testPerformanceExample {
// This is an example of a performance test case.
// 测试性能例子
[self measureBlock:^{
// Put the code you want to measure the time of here.
// 需要测试性能的代码
}];
}
在ViewController中写一个简单的方法
-(int)getNum;
实现:
- (int)getNum {
return 100;
}
在测试的文件中导入ViewController.h,并且定义一个vc属性
#import
#import "ViewController.h"
@interface ____Tests : XCTestCase
@property (nonatomic,strong) ViewController *vc;
@end
@implementation ____Tests
//测试用例的实现
- (void)setUp {
[super setUp];
// 实例化需要测试的类
self.vc = [[ViewController alloc] init];
}
- (void)tearDown {
// 清空
self.vc = nil;
[super tearDown];
}
- (void)testMyFuc {
// 调用需要测试的方法,
int result = [self.vc getNum];
// 如果不相等则会提示@“测试不通过”
XCTAssertEqual(result, 100,@"测试不通过");
}
command+u快捷方式运行,或者produce-->test都行,或只跑某个测试用例如图
性能测试
3.使用的断言种类
1.XCTFail(format…) 生成一个失败的测试;
2.XCTAssertNil(a1, format...)为空判断,a1为空时通过,反之不通过;
3.XCTAssertNotNil(a1, format…)不为空判断,a1不为空时通过,反之不通过;
4.XCTAssert(expression, format...)当expression求值为TRUE时通过;
5.XCTAssertTrue(expression, format...)当expression求值为TRUE时通过;
6.XCTAssertFalse(expression, format...)当expression求值为False时通过;
7.XCTAssertEqualObjects(a1, a2, format...)判断相等,[a1 isEqual:a2]值为TRUE时通过,其中一个不为空时,不通过;
8.XCTAssertNotEqualObjects(a1, a2, format...)判断不等,[a1 isEqual:a2]值为False时通过;
9.XCTAssertEqual(a1, a2, format...)判断相等(当a1和a2是 C语言标量、结构体或联合体时使用,实际测试发现NSString也可以);
10.XCTAssertNotEqual(a1, a2, format...)判断不等(当a1和a2是 C语言标量、结构体或联合体时使用);
11.XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)判断相等,(double或float类型)提供一个误差范围,当在误差范围(+/-accuracy)以内相等时通过测试;
12.XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...) 判断不等,(double或float类型)提供一个误差范围,当在误差范围以内不等时通过测试;
13.XCTAssertThrows(expression, format...)异常测试,当expression发生异常时通过;反之不通过;(很变态)
14.XCTAssertThrowsSpecific(expression, specificException, format...) 异常测试,当expression发生specificException异常时通过;反之发生其他异常或不发生异常均不通过;
15.XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression发生具体异常、具体异常名称的异常时通过测试,反之不通过;
16.XCTAssertNoThrow(expression, format…)异常测试,当expression没有发生异常时通过测试;
17.XCTAssertNoThrowSpecific(expression, specificException, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过;
18.XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过
特别注意下XCTAssertEqualObjects和XCTAssertEqual。
XCTAssertEqualObjects(a1, a2, format...)的判断条件是[a1 isEqual:a2]是否返回一个YES。
XCTAssertEqual(a1, a2, format...)的判断条件是a1 == a2是否返回一个YES。
对于后者,如果a1和a2都是基本数据类型变量,那么只有a1 == a2才会返回YES。
4.异步函数的单元测试
//waitForExpectationsWithTimeout是等待时间,超过了就不再等待往下执行。
#define WAIT do {\
[self expectationForNotification:@"RSBaseTest" object:nil handler:nil];\
[self waitForExpectationsWithTimeout:30 handler:nil];\
} while (0);
#define NOTIFY \
[[NSNotificationCenter defaultCenter]postNotificationName:@"RSBaseTest" object:nil];
增加测试方法testRequest:
-(void)testRequest{
// 1.获得请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
mgr.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",nil];
// 2.发送GET请求
[mgr GET:@"http://www.weather.com.cn/adat/sk/101110101.html" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"responseObject:%@",responseObject);
XCTAssertNotNil(responseObject, @"返回出错");
NOTIFY //继续执行
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"error:%@",error);
XCTAssertNil(error, @"请求出错");
NOTIFY //继续执行
}];
WAIT//暂停
}
5.测试覆盖率的查看
在运行测试之前,我们必须先确认 code coverage 是否被打开了,写代码时,默认是关闭的。所以你需要编辑一下你的测试 scheme,把它打开。
确保"Gather coverage data"是被选中的,然后点击关闭按钮,运行测试的 target. 我们希望刚刚创建的测试用例能够顺利通过。
Coverage Tab
一旦这个测试通过了,你就能知道 checkWord 这个方法,至少有一条路径是对的。但你不知道的是,还多多少没有被测试到。这就是code coverage这个工具的好处。当你打开code coverage tab后,你可以清楚的看到测试的覆盖情况。他们按找 target, file, function 进行了自动分组。
打开Xcode左边窗口的Report Navigator面板,选中你刚运行的测试。然后在tab中选中 Coverage。
这会展示一个你的类、方法的列表,并标示出每个的测试覆盖率。如果你将鼠标悬停在checkWord这个方法上,你可以看到测试的覆盖率是28%。不能接受啊!我们需要找到,那些代码分支是能够被测试执行,那些是不能的,进而改善他们。双击方法的名字,Xcode会打开类的代码,并且看到code coverage的情况。
白色的区域表示这些代码时测试覆盖过的。灰色区域时测试无法覆盖的,我们需要添加更多的测试用例来覆盖灰色部分的代码。在右手边的数字,表明这些代码块,在这次测试中被执行的次数。
UI测试
可以参考这个网站 http://www.cocoachina.com/ios/20150702/12253.html 这里就不啰嗦了
总结UI测试遇到的问题 (重点)
1.自动生成uitests字符乱码问题
目前查阅资料只有\U变成\u,可以用查找工具替换掉,生成的unicode编码也可以直接用中文替换;
2.uitests自动生成的代码经常报TimeStamped Event Matching Error:Failed to find matching element ,这个大多数原因是由于控件id变化导致的,所以对于id动态变化一般都通过下标形式查找到相应的控件
Xcode自动产生的大多数是这样:
[app.buttons[@"\u4e8c\u624b\u8f66\u4e16\u754c"] tap];
Demo:
[[[[[app.tables elementBoundByIndex:0] childrenMatchingType:XCUIElementTypeCell] elementBoundByIndex:4] childrenMatchingType:XCUIElementTypeTextField] elementBoundByIndex:0]
[app.XXX];获得当前window上所有这种类型的控件
3.uitests textFiled不能通过identifier直接获取到,利用录制的话一般以点击就会报错误;
4.descendantsMatchingType查找包含子类,childrenMatchingType查找本类不包含子类;
5.获取的控件不知道存不存在时可以用exists判断下,对没有获取到的控件操作直接会崩;
6.要特别注意弹出的视图,如果此时对后面一层视图操作会出问题,如果能自动消失的,可以使用延迟pressForDuration: (NSTimeInterval)后在操作,如果不会消失的要转到这层操作;
7.Assertion Failure: :0: UI Testing Failure - Failed to scroll to visible (by AX action) Image 0x600000175240: traits: 8589934596, {{172.0, 70.0}, {70.0, 70.0}}, identifier: 'Icon_Boy', error: Error -25204 performing AXAction 2003 //没有滚到可见范围的错误
[[[[[self.app.tables elementBoundByIndex:0] descendantsMatchingType:XCUIElementTypeAny] elementBoundByIndex:0]tap]; 报上面的错误
解决:[[[[[self.app.tables elementBoundByIndex:0] descendantsMatchingType:XCUIElementTypeAny] elementBoundByIndex:0] coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)] tap];
coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5) 使用这个函数增加点偏移量即可,我试了0.5,0.5 可以用,你们可以试试别的行不行,目前我还不知道这个值是怎么确定的,我们需要做判断用这个方法时,可以用isHittable这个判断下是否可以点击,如果是false,就用偏移的方法,如果返回为yes,就原来的方法即可
有时候放在tableview头视图的imageView,使用[[self.app.images elementBoundByIndex:0] tap]产生这种错误时,可以使用[[self.app.tables elementBoundByIndex:0] descendantsMatchingType:XCUIElementTypeAny] elementBoundByIndex:0] tap]解决
有任何问题可以@我哦
参考网址
http://www.jianshu.com/p/009844a0b9edUnitTest(简单的单元测试使用)
http://blog.csdn.net/it_ds/article/details/51286791UnitTest(简单的单元测试使用)
http://www.jianshu.com/p/8bbec078cabeUnitTest(有异步测试Demo)
http://www.jianshu.com/p/254d6f9f0bc4UnitTest(利用GCD实现异步测试Demo)
http://www.cocoachina.com/ios/20150702/12253.html UITest
http://blog.sina.com.cn/s/blog_864456e50101ekqh.html断言解释说明
http://www.cnblogs.com/ziyi--caolu/p/4893847.htmlUnitTest(有测试实例代码)
http://www.cocoachina.com/ios/20150915/13163.html单元测试覆盖率
https://pan.baidu.com/s/1eR112i2自动化测试视频地址