iOS异步测试

284 查看

题外话 首先恭喜AlphaGo赢得与李世石九段五番棋的首番,作为下围棋的程序员,我能感受到这盘棋的震撼。其次要夸奖一下segmentfault的新版界面做的很漂亮,如果能配乐就更好了。本文配乐是《最善の一手》。


决定伟大水平和一般水平的关键因素,既不是天赋,也不是经验,而是“刻意练习“的程度。

刻意练习是指为了提高绩效而被刻意设计出来的练习,它要求一个人离开自己的熟练和舒适区域,不断地依据方法去练习和提高。

比如足球爱好者只不过是享受踢球的过程,普通的足球运动员只不过是例行惯事地训练和参加比赛,而顶尖的足球运动员却不断地发现现有能力的不足,并且不断以自己不舒服的方式挑战并练习高难度的动作。

阅读原文

Xcode集成了XCTest测试框架,新建iOS项目的时候,勾选Include Unit Tests,可以建立一个名叫$(PROJECT_NAME)Tests的测试项目。使用XCTest框架进行常规单元测试很简单,就不做讲解了,这里我们主要关注异步测试。

问题是这样的。先看下面的单元测试

-(void)testAsyncTask
{
    NSURLSession* session=[NSURLSession sharedSession];
    NSURL* url=[NSURL URLWithString:@"https://segmentfault.com"];
    NSURLRequest* request=[NSURLRequest requestWithURL:url];
    NSURLSessionTask* task=[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        XCTAssertTrue(!error);
    }];
    [task resume];
}

测试的是一个异步网络任务,在回调XCTAssertTrue(!error)处打断点,然后运行测试。
运行结果是Test Succeeded,但是回调没有执行,我们的测试也就没有起到预期的作用。

问题出在哪呢?

当执行完[task resume]后,测试方法就返回了,主线程随之结束,测试程序也就结束了,不会等待异步任务执行完成。

如何解决呢?

如果读者使用cocoapods做依赖管理,可以引入一个名叫JXAsyncTest的异步测试工具pod,在Podfile里添加如下代码

target 'XXXXTest'

pod 'JXAsyncTest'

end

执行命令pod install --verbose
执行成功就完成了JXAsyncTest的安装,然后我们在测试类里引入头文件

#import <JXAsyncTest/JXAsyncTest.h>

对上面的测试方法做如下修改

-(void)testAsyncTask
{
    JXAsyncTest* asyncTest=[[JXAsyncTest alloc] initWithTimeoutSeconds:5];
    BOOL passed=[asyncTest runTest:^(JXAsyncTest* asyncTest){
        NSURLSession* session=[NSURLSession sharedSession];
        NSURL* url=[NSURL URLWithString:@"https://segmentfault.com"];
        NSURLRequest* request=[NSURLRequest requestWithURL:url];
        NSURLSessionTask* task=[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            XCTAssert(!error);
            [asyncTest pass];
        }];
        [task resume];
    }];
    XCTAssert(passed);
}

仍然在回调处打断点,并运行测试,可以看到这一次回调得到了执行。
有兴趣的读者可以阅读JXAsyncTest的实现。