在 XCTests 中使用 AFHTTPRequestOperation

Posted

技术标签:

【中文标题】在 XCTests 中使用 AFHTTPRequestOperation【英文标题】:Using AFHTTPRequestOperation in XCTests 【发布时间】:2015-01-17 13:26:04 【问题描述】:

我有以下测试用例代码:

- (void)testExample 
    // URL https://api.spotify.com/v1/search?q=album%3AJustified%20artist%3AJustin%20Timberlake&type=album


    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    [[AFHTTPRequestOperationManager manager] GET:@"https://api.spotify.com/v1/search"
                                      parameters:@@"q":@"album:Justified artist:Justin Timberlake",
                                                   @"type":@"album"
                                         success:^(AFHTTPRequestOperation *operation, id responseObject) 
                                             dispatch_semaphore_signal(sem);
                                          failure:^(AFHTTPRequestOperation *operation, NSError *error) 

                                         
     ];
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

    // This is an example of a functional test case.
    XCTAssert(YES, @"Pass");

我期待测试用例阻塞并等待 http 请求完成。 奇怪的是,即使 url 是有效的,AFHTTPRequestOperation 也永远不会到达成功块。 如果我在 XCTest 之外使用以下代码,则不会发生,将执行成功块。 有没有人见过这个?

【问题讨论】:

【参考方案1】:

几个观察:

    您的测试正在冻结,因为 AFNetworking 将其完成块分派到主队列。但是你用dispatch_semaphore_wait 阻塞了主线程,导致死锁。

    您可以通过将管理器的completionQueue 设置为用于测试的全局队列来解决此问题,从而消除由主线程上的信号量引起的死锁:

    - (void)testExample 
        dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
        manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
        [manager GET:@"https://api.spotify.com/v1/search" parameters:@@"q":@"album:Justified artist:Justin Timberlake", @"type":@"album" success:^(AFHTTPRequestOperation *operation, id responseObject) 
            XCTAssert(YES, @"Pass"); // you might want more rigorous test of results here
    
            dispatch_semaphore_signal(sem);
         failure:^(AFHTTPRequestOperation *operation, NSError *error) 
            XCTFail(@"%@", error.localizedDescription);
    
            dispatch_semaphore_signal(sem);
        ];
    
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    
    

    注意,现在人们会使用XCTestExpectation 来执行异步测试。这消除了对信号量的需求,巧合的是,也解决了死锁问题:

    - (void)testExample 
        XCTestExpectation *expectation = [self expectationWithDescription:@"asynchronous request"];
    
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
        [manager GET:@"https://api.spotify.com/v1/search" parameters:@@"q":@"album:Justified artist:Justin Timberlake", @"type":@"album" success:^(AFHTTPRequestOperation *operation, id responseObject) 
            XCTAssert(YES, @"Pass"); // you might want more rigorous test of results here
    
            [expectation fulfill];
         failure:^(AFHTTPRequestOperation *operation, NSError *error) 
            XCTFail(@"%@", error.localizedDescription);
    
            [expectation fulfill];
        ];
    
        [self waitForExpectationsWithTimeout:30.0 handler:nil];
    
    

    顺便说一句,无论您使用XCTestExpectation 还是信号量,请确保成功和失败块都满足期望/信号量。

【讨论】:

是的,我认为第二种解决方案对我有用。谢谢

以上是关于在 XCTests 中使用 AFHTTPRequestOperation的主要内容,如果未能解决你的问题,请参考以下文章

我应该重构以便能够使用 XCTests 进行模拟吗?

XCTests 过早取消

XCTests 中的核心数据

单例不使用 XCTests - iOS

在运行 XCTests 时访问主应用程序包

如何修复 Xcode 10 中的“XCtests 间歇性无法在模拟器中启动应用程序”