很多开发者都听说过单元测试,但是在实际开发中我们却很少使用到单元测试,一般流程规范的大项目一般都会做单元测试,下面主要介绍 Xcode 自带的 XCTest 测试工具。
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。如C语言中的一个函数,Java里的一个类等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。
// 此方法每次测试前调用
(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.
}
// 性能测试方法,通过测试block中方法执行的时间,比对设定的标准值和偏差觉得是否可以通过测试
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
XCTFail(format…) //生成一个失败的测试;
XCTAssertNil(a1, format…) //为空判断, a1 为空时通过,反之不通过;
XCTAssertNotNil(a1, format…) //不为空判断,a1不为空时通过,反之不通过;
XCTAssert(expression, format…) //当expression求值为TRUE时通过;
XCTAssertTrue(expression, format…) //当expression求值为TRUE时通过;
XCTAssertFalse(expression, format…) //当expression求值为False时通过;
XCTAssertEqualObjects(a1, a2, format…) //判断相等, [a1 isEqual:a2] 值为TRUE时通过,其中一个不为空时,不通过;
XCTAssertNotEqualObjects(a1, a2, format…) //判断不等, [a1 isEqual:a2] 值为False时通过,
XCTAssertEqual(a1, a2, format…) //判断相等(当a1和a2是 C语言标量、结构体或联合体时使用,实际测试发现NSString也可以);
XCTAssertNotEqual(a1, a2, format…) //判断不等(当a1和a2是 C语言标量、结构体或联合体时使用);
XCTAssertEqualWithAccuracy(a1, a2, accuracy, format…) //判断相等,(double或float类型)提供一个误差范围,当在误差范围(+/- accuracy )以内相等时通过测试;
XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format…)//判断不等,(double或float类型)提供一个误差范围,当在误差范围以内不等时通过测试;
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没有发生具体异常、具体异常名称的异常时通过测试,反之不通过;
- (void)testNumEqual {
NSInteger result = 2 * 3;
NSInteger expect = 6;
XCTAssertEqual(result, expect, @"结果和预期不相等");
}
- (void)testPerformanceExample {
// This is an example of a performance test case.
NSLog(@"性能测试");
// 用来分析代码执行的时间(会执行10次),log会打印在 console 里,同时本地也会有一份日志
[self measureBlock:^{
// Put the code you want to measure the time of here.
for (NSInteger index = 0; index < 10000; index ++){
NSLog(@"%ld",index);
}
}];
}
基准线和偏差是可以设置的,如果偏差10%,只要平均值在基准线的偏差10%都是可以通过的测试的。但是如果设置如果我们把baseline为1,偏差10%,那测试会失败,因为不满足条件。
/// 期望
- (void)testAsynExample {
XCTestExpectation *exp = [self expectationWithDescription:@"这里可以是操作出错的原因描述。"];
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
// 模拟这个异步操作需要2秒后才能获取结果,比如一个异步网络请求
sleep(2);
// 模拟获取的异步操作后,获取结果,判断异步方法的结果是否符合预期
NSString *result = @"dddd";
NSString *expect = @"dddd";
XCTAssertEqual(result, expect);
// 如果断言没问题,就调用fulfill宣布测试满足
[exp fulfill];
}];
//设置延迟多少秒后,如果没有满足测试条件就报错
[self waitForExpectationsWithTimeout:3 handler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Timeout Error: %@", error);
}
}];
}
异步测试除了使用 expectationWithDescription以外,还可以使用 expectationForPredicate 和 expectationForNotification。
- (void)testLogin {
XCUIApplication *app = [[XCUIApplication alloc] init];
// XCUIElement 这是 UI 元素的代理。元素都有类型和唯一标识。可以结合使用来找到元素在哪里,如当前界面上的一个输入框
XCUIElementQuery *textFields = app.textFields;
XCUIElement *usernameTextField = textFields[@"username:"];
[usernameTextField tap];
[usernameTextField typeText:@"Andy"];
XCUIElement *passwordTextField = app.textFields[@"password:"];
[passwordTextField tap];
[passwordTextField typeText:@"123456"];
//
[app.buttons[@"login"] tap];
// 登录成功后的控制器的title为用户名,只需判断控制器的title时候一样便可判断登录是否成功
NSLog(@"title is %@",app.navigationBars.element.identifier);
XCTAssertEqualObjects(app.navigationBars.element.identifier, @"Andy");
}
XCUIElement 和 XCUIElementQuery 是UI测试的两个核心类。
代码覆盖是LLVM提供的的测试选项。 当您启用代码覆盖时,LLVM将根据调用方法和函数的频率,对代码收集覆盖数据。 代码覆盖选项可以收集数据以报告正确性和性能的测试,无论是单元测试还是UI测试。
如果你有development-enabled设备插入,你可以按照名称或 id 调用他们。例如,如果你有一个名为”Development iPod touch”的 iPod 设备连接了测试的代码,可以使用下面的命令来测试代码 > xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=iOS,name=Development iPod touch
测试也可以在 iOS模拟器上运行。使用模拟器可以应对不同的外形因素和操作系统版本。例如> xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination ‘platform=iOS Simulator,name=iPhone,OS=8.0’
-destination 参数可以被连接在一起,这样你只需使用一个命令,就可以跨目标进行指定集成共享方案。例如,下面的命令把之前的三个例子合并到一个命令中。
> xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp
-destination 'platform=OS X,arch=x86_64'
-destination 'platform=iOS,name=Development iPod touch'
-destination 'platform=iOS Simulator,name=iPhone,0S=7.0'
参考: