XCTest:NSURLSession:主线程上的停滞

XCTest: NSURLSession: Stall on main thread

提问人:FranticRock 提问时间:7/9/2016 最后编辑:CommunityFranticRock 更新时间:7/9/2016 访问量:3284

问:

运行执行网络操作的 XCTest 时,出现以下错误:

-waitForExpectationsWithTimeout:超时,但无法运行超时处理程序,因为主线程无响应(等待超时后允许 0.5 秒)。可能导致这种情况的情况包括主线程上的处理阻塞 IO、对 sleep() 的调用、死锁和同步 IPC。Xcode 将尝试重新启动该过程并继续下一次测试...

我有一个测试,它执行一个网络命令方法,该方法在后台使用 NSURLSession。

此测试是具有 4 个连续网络请求测试的测试类的一部分。

当独立于其他测试运行时,它每次都会成功。 运行整个测试文件(总共 4 个测试)时,每次都成功。

但是当运行整个测试套件(50 个测试)时,我在第一次网络操作测试中出现“主线程失速”。其他 3 项网络操作测试通过良好。

以下是测试的代码:

- (void)test_session_001_ObtainsSessionObjectFromServer
{
    XCTestExpectation *expectation = [self expectationWithDescription:[self description]];
    
    id<IPVideoSessionProtocol> provider = [IPVideoSessionFactory getProvider];
    
    [provider createVideoSessionWithUserID:@"SomeUserID"
        withCompletionBlock:^(IPVideoSession *session) {
        
        if ([session isKindOfClass:[IPVideoSession class]] &&
            [session.sessionID isKindOfClass:[NSString class]] &&
            session.sessionID.length > 0) {
            
            // The returned session ID is populated
            [expectation fulfill];
        }
    }];
    
    [self waitForExpectationsWithTimeout:5.0f handler:^(NSError *error) {
        if (error)
        {
            XCTFail(@"IPVideoSessionManagerTests Did not execute in allotted time");
        }
    }];
}

createVideoSession中的代码...

- (void)createVideoSessionWithUserID:(NSString*)userID
    withCompletionBlock:(IPVideoSessionCompletionBlock)block
{
    IPCreateVideoSessionCommand* command = [[IPCreateVideoSessionCommand alloc]
        initWithUserID:userID];
    
    [command executeWithCompletionBlock:^(IPNetworkError *error, NSString *resultJSON)
    {
        if (error) {
            [IPAlertPresenter displayNetworkError:error];
            block(nil);
            return;
        }
        
        NSDictionary *dict = [resultJSON toJsonDictionary];
        block([IPVideoSession getSessionFromDictionary:dict]);
    }];
}

executeWithCompletionBlock 中的代码:

- (void)executeWithCompletionBlock:(CommandCompletionBlock)block
{
    if (!self.isConnected && ![[IPDebugManager sharedInstance] networkBypassActivated]) {
        IPNetworkError* error = [[IPNetworkError alloc] initWithType:NetworkErrorTypeNotConnectedToNetwork];
        block(error, nil);
        return;
    }
    
    NSURLSession *session = [self composeSession];
    NSURLRequest *request = [self composeRequest];
    
    self.strongSelf = self;
    __weak __typeof(self) weakSelf = self;
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        [IPDispatch toMainThread:^{
            [weakSelf
                handleCommandResponse:response
                withData:data
                withError:error
                withCompletionBlock:block];

            weakSelf.strongSelf = nil;
        }];
    }];
    
    [dataTask resume];
}

注意:仅当运行所有测试时,才会发生此错误。这个测试本身就成功了,而且:如果我在这个测试类中运行所有 4 个测试。

Xcode 异步 超时 nsurlsession xctest

评论

1赞 dgatwood 8/3/2016
一般来说,网络测试是有问题的,当你将代码弹回主线程时,情况会更加严重。在不知道 IPDispatch 的 toMainThread 方法的作用的情况下,我的第一个猜测是这里的一些代码试图将已经在主线程上运行的东西同步调度到主线程,并且正在阻塞自身。
0赞 FranticRock 8/9/2016
这是我得到的一些其他异常信息:“超时,但无法运行超时处理程序,因为主线程无响应(等待超时后允许 0.5 秒)” 另外,添加到您对 IPDispatch 的评论中: 它有一个检查,检查我们是否已经在主线程上,如果是, 直接调用块即可,否则 Dispatch 到主线程上即可。因此,“Is Main Thread”检查已经处理完毕。
0赞 FranticRock 8/9/2016
另一个发现:我每次都在一个模拟器 iPhone 6 plus (GUID A) 上不断收到此错误。我切换到另一个模拟器:iPhone 6 plus(GUID B),整个测试套件每次都运行良好。我尝试完全重置两个模拟器,并进行了清理。这没什么区别。因此,测试状态污染似乎不是问题所在。
1赞 Nicolas Jakubowski 9/23/2016
你有没有找到这个问题的解决方案?我面临着同样的情况,让我发疯!
1赞 FranticRock 12/22/2016
不,当运行使用 waitForExpectationsWithTimeout 的异步测试时,我仍然在 XCTest 中收到“Stall on Main Thread”。在很多情况下,问题仅在我运行整个测试套件时才会发生。单独运行测试时,它可以工作。

答: 暂无答案