iOS输入法—基于XCTest框架的异步测试

来源:互联网 发布:宇宙诞生之前知乎 编辑:程序博客网 时间:2024/06/04 08:45

苹果今年在Xcode 6中添加了XCTest框架此前漏掉的几项功能,这当中的异步测试功能大大方便了我们的测试工作。

如果我们的测试项目要执行一个异步任务,它可能会跑在其它的线程里,也可能会跑在主线程的RunLoop里,在这种时候我们应该如何去进行测试呢?来看一个非常简单的例子!

现在有一个web请求的功能需要测试。我们会开始web请求然后进入阻塞,接下来在程序完成的代码块中做一个测试断言。然而,鉴于没有web请求的情况下更谈不上响应,为了调用程序完成部分的代码我们就需要在断言之前确保被测试的方法已经在执行。

在通常情况下我们不会真的在测试时去做web响应,而是用一些工具来中断响应。不过在下面的例子里面我们需要更改一下规则——真正的完成一个web响应来进行测试。

我们使用了一个URL和一个程序完成块来模拟这个类的一个方法,它会下载URL所指的页面并调用这个程序块,如果成功会得到一个包含web页面的字符串,而发生错误的时候则是一个空字符串。


- (void)testCodeYouShouldNeverWrite

{

__block NSString *pageContents = nil;

[self.pageLoader requestUrl:@" http://123.sogou.com "

completionHandler:^(NSString *page) {

NSLog(@"The web page is %ld bytes long.", page.length);

// Test method ends before this test assertion is called

XCTAssert(pageContents.length > 0);

pageContents = page;

}];

// Nothing prevents the test method from returning before

// completionHandler is called.

}

很显然,这种测试方法是有问题的!

Xcode 6之前的版本里面并没有内置XCTest,想使用Xcode测试的只能是在主线程的RunLoop里面使用一个while循环,然后一直等待响应或者直到timeout,下面就是这种旧方法的代码:


- (void)testAsyncTheOldWay

{

NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:5.0];

__block BOOL responseHasArrived = NO;

[self.pageLoader requestUrl:@" http://123.sogou.com"

completionHandler:^(NSString *page) {

NSLog(@"The web page is %ld bytes long.", page.length);

responseHasArrived = YES;

XCTAssert(page.length > 0);

}];

while (responseHasArrived == NO && ([timeoutDate timeIntervalSinceNow] > 0)) {

CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.01, YES);

}

if (responseHasArrived == NO) {

XCTFail(@"Test timed out");

}

}

这个while循环在主线程里面每隔10毫秒会跑一次,直到有响应或者5秒之后超出响应时间限制才会跳出。这个方法十分有效而且看上去也不糟,但这并不意味着开发的终结——这个方法还是不够好。接下来说一个更加优化的办法。

Xcode 6里,苹果以XCTestExpection类的方式向XCTest框架里添加了测试期望(test expection)。当我们实例化一个测试期望(XCTestExpectation)的时候,测试框架就会预计它在之后的某一时刻被实现。最终的程序完成代码块中的测试代码会调用XCTestExpection类中的fulfill方法来实现期望。

这一方法替代了我们之前例子里面使用responseHasArrived作为Flag的方式,这时我们让测试框架等待(有时限)测试期望通过XCTestCasewaitForExpectationsWithTimeout:handler:方法实现。如果完成处理的代码在指定时限里执行并调用了fulfill方法,那么就说明所有的测试期望在此期间都已经被实现。否则,这个测试就悲剧了,它会默默的存在程序中而不会被实现哪怕一次……

当然,失败结果并不意味着失败的测试,只有不明就里的测试结果才算失败的测试。

下面是使用XCTestExpection的示例:


(void)testWebPageDownload

{

XCTestExpectation *expectation =

[self expectationWithDescription:@"High Expectations"];

[self.pageLoader requestUrl:@"http://123.sogou.com”

completionHandler:^(NSString *page) {

NSLog(@"The web page is %ld bytes long.", page.length);

XCTAssert(page.length > 0);

[expectation fulfill];

}];

[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {

if (error) {

NSLog(@"Timeout Error: %@", error);

}

}];

}

在最后的代码段里面使用[expectation fulfill]来告知此次测试所期望的部分已经确切实现过了。

然后用waitForExpectationsWithTimeout:handler方法等待响应,这段会在接受响应之后执行……或者超时之后也会执行。



原文链接

如需转载该篇文章,请注明来自“搜狗测试”


0 0
原创粉丝点击