编写用例时注意
1.用例只针对SDK对外开放的入口
2.注意异常情况和边界值
3.注意用例之间的依赖情况
单测用例如何按照顺序执行
测试函数的执行顺序与测试的字符大小有关系,如- (void)test001Example > - (void)test002Example > - (void)testExample
理论上单元测试不应该控制用例的执行顺序,每个case都能够单独执行,但是针对某些特殊的情况,可以适当使用一次此特性。
用例依赖的情况如何处理
当你的用例需要按照顺序执行就说明你的用例存在依赖,针对这种情况就需要进行拆分,放到不同的组(测试文件)当中来执行,比如说存在a,b,c三个用例,b和c都依赖于a,那么比较合理的做法就是将a提到另外一个文件当中进行测试,b和c放到另外一个文件中,在b和c的文件中将a放入setUp方法用作b、c执行的前提,这样就能比较好的进行测试,也减少了大量的冗余代码。
multiple calls made to -[XCTestExpectation fulfill]
出现这种情况是因为写在回调当中的XCTestExpectation被多次调用导致的,解决这个错误,网上目前有以下三种解决方案,可以根据自己的情况进行选择:
1.使用__weak修饰XCTestExpectation。
[expectation fulfill]
执行一次后方法执行完毕,局部变量expectation被释放,当回调再次调起时,expectation已被释放置nil,执行fulfill
不会报错。
- (void)testExample {
NSString *username = @"shuhai";
NSString *password = @"123";
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"登录请求返回"];
[SHUserCenter loginWithUserName: username password: password callback:^(NSDictionary *data) {
XCTAssertNotNil([data objectForKey:@"data"],@"返回结果不能为nil");
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:self.timeout handler:nil];
}
但是该种方法存在一个问题,就是当有多个XCTestExpectation时,靠前的expectation执行fulfill
时,该方法并不一定结束,导致再次调用fulfill
报错,如下当noticeExpectation所在的回调被第二次调用时,expectation可能并未fulfill
,变回导致noticeExpectation多次fulfill
报错。
- (void)testExample {
NSString *username = @"shuhai";
NSString *password = @"123";
__weak XCTestExpectation *noticeExpectation = [self expectationWithDescription:@"登录通知返回"];
[SHUserCenter registerNotice:SHUCenterNoticeTypeLoginSuccess handle:^(NSDictionary *data) {
XCTAssertNotNil([data objectForKey:@"data"],@"返回结果不能为nil");
[noticeExpectation fulfill];
}];
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"登录请求返回"];
[SHUserCenter loginWithUserName: username password: password callback:^(NSDictionary *data) {
XCTAssertNotNil([data objectForKey:@"data"],@"返回结果不能为nil");
[expectation fulfill];
}];
[self waitForExpectations:@[noticeExpectation,expectation] timeout:self.timeout];
}
2.使用__block修饰XCTestExpectation并在fulfill之后置为nil
当XCTestExpectation对象执行过fulfill
之后被置为了nil,再次调用fulfill
则不会报错。
- (void)testExample {
NSString *username = @"shuhai";
NSString *password = @"123";
__block XCTestExpectation *expectation = [self expectationWithDescription:@"登录请求返回"];
[SHUserCenter loginWithUserName: username password: password callback:^(NSDictionary *data) {
XCTAssertNotNil([data objectForKey:@"data"],@"返回结果不能为nil");
[expectation fulfill];
expectation = nil;
}];
[self waitForExpectationsWithTimeout:self.timeout handler:nil];
}
这种方法在大部分情况可以可以解决问题,有种特殊的情况我们后面再讨论
3.移除回调的多次调用逻辑。
查找并移除回调多次调用的逻辑是从根本上解决这种问题的方法,但是某些业务场景下我们可能是需要回调多次调用的,针对这种情况,我们就可以采用第二种方法来解决问题。
超时之后fulfill错误无法上报
这种错误是因为等待XCTestExpectation超时测试方法执行完毕后,回调调用(比如网络不好超时的情况),而且返回了一个错误的结果,在回调当中的断言失败却无法上报错误导致的崩溃,错误信息大致如下:
Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Unable to report test assertion failure '((data) != nil)
failed: throwing "Unable to report test assertion failure '((data) != nil) failed' from /Users/yzq/Documents/Component/CHLUserCenter/Example/Tests/CHLUserCenterNoDataTests.m:31 because it was raised inside test case -[CHLUserCenterNoDataTests test001] which has no associated XCTestRun object.
This may happen when test cases are constructed and invoked independently of standard XCTest infrastructure, or when the test has already finished."
' from /Users/yzq/Documents/Component/CHLUserCenter/Example/Tests/CHLUserCenterNoDataTests.m:31 because it was raised inside test case -[CHLUserCenterNoDataTests test001] which has no associated XCTestRun object.
This may happen when test cases are constructed and invoked independently of standard XCTest infrastructure, or when the test has already finished.'
这种错误在上面提到的使用__block的情况下也不能幸免,解决方案就是使用一个__block修饰的变量,将回调当中要获取的值取出来,然后再外层进行数据判断,这样就不会导致断言在回调当中崩溃了。
- (void)testExample {
NSString *username = @"shuhai";
NSString *password = @"123";
__block XCTestExpectation *expectation = [self expectationWithDescription:@"登录请求返回"];
__block NSDictionary *returnData = nil;
[SHUserCenter loginWithUserName: username password: password callback:^(NSDictionary *data) {
returnData = data;
[expectation fulfill];
expectation = nil;
}];
[self waitForExpectationsWithTimeout:self.timeout handler:nil];
XCTAssertNotNil([returnData objectForKey:@"data"],@"返回结果不能为nil");
}
Assertion failure in -[XCTestExpectation fulfill]
这个错误是当有多个XCTestExpectation时使用waitForExpectations:timeout:
等待Expectation导致,导致错误的具体原因目前还不是很明确,有了解具体原因的同学可以告诉我下。
针对这个问题,我们可以使用waitForExpectationsWithTimeout:handler:
来等待Expectation,这样就不会报错了。
attempt to insert nil object from objects[0]
这个错误是因为我们在前面使用__block修饰XCTestExpectation并置为nil操作的解决方案时同时使用了waitForExpectations:timeout:
来等待Expectation,向数组中插入了nil。
这个问题的解决方案同上,使用waitForExpectationsWithTimeout:handler:
来替换即可。所以针对大多数啊情况推荐使用waitForExpectationsWithTimeout:handler:
来等待XCTestExpectation。