在 XCTests 中使用 AFHTTPRequestOperation
Using AFHTTPRequestOperation in XCTests
我有以下测试用例代码:
- (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 请求完成。
奇怪的是 AFHTTPRequestOperation
永远不会到达成功块,即使 url 是有效块。
如果我在 XCTest 之外使用以下代码,则不会发生,将执行成功块。
有人以前看过这个吗?
一些观察:
您的测试正在冻结,因为 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
还是semaphore,都要确保成功块和失败块都满足expectation/semaphore.
我有以下测试用例代码:
- (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 请求完成。
奇怪的是 AFHTTPRequestOperation
永远不会到达成功块,即使 url 是有效块。
如果我在 XCTest 之外使用以下代码,则不会发生,将执行成功块。
有人以前看过这个吗?
一些观察:
您的测试正在冻结,因为 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
还是semaphore,都要确保成功块和失败块都满足expectation/semaphore.