AFNetworking 2.0 和单元测试
Posted
技术标签:
【中文标题】AFNetworking 2.0 和单元测试【英文标题】:AFNetworking 2.0 and Unit Test 【发布时间】:2014-04-29 21:40:09 【问题描述】:我正在尝试使用 XCTest 在 XCode 5 中对使用 AFNEtworking 的类进行单元测试。我遇到的问题是我的 AFHTTPRequestOperation 的完成块永远不会被执行。我认为这是运行单元测试的 XCode 和 AFNetworking 的调度队列之间的一些脱节。以下测试用例通过但从未到达完成块中的 NSLog 语句(没有日志输出,也没有捕获在这些语句上设置的断点)。相同的代码在单元测试之外工作。有谁知道如何解决这个问题?我使用 Nocilla 模拟实际请求,结果与使用真实服务器重新调整有效响应的结果相同?
已编辑以使测试失败并记录块中设置的变量
- (void)setUp
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
[[LSNocilla sharedInstance] start];
stubRequest(@"POST", @"http://www.example.com/module/api/ping").
andReturn(200).
withHeaders(@@"Content-Type": @"application/json").
withBody(@"\"success\":true");
stubRequest(@"GET", @"http://www.example.com/module/api/ping?testkey=testval").
andReturn(200).
withHeaders(@@"Content-Type": @"application/json").
withBody(@"\"success\":true");
- (void)tearDown
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
[[LSNocilla sharedInstance] stop];
[[LSNocilla sharedInstance] clearStubs];
- (void)testSanity
AFSecurityPolicy *policy = [[AFSecurityPolicy alloc] init];
//[policy setAllowInvalidCertificates:YES];
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:@"http://www.example.com/module/api/ping"]];
//manager.operationQueue = [NSOperationQueue mainQueue];
[manager setSecurityPolicy:policy];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
__block id resObj = nil;
__block id resError = nil;
AFHTTPRequestOperation *req = [manager POST:@"http://www.example.com/module/api/ping"
parameters:[NSDictionary dictionaryWithObject:@"testval" forKey:@"testkey"]
success:^(AFHTTPRequestOperation *operation, id responseObject)
NSLog(@"Response: %@", responseObject);
resObj = responseObject;
return;
failure:^(AFHTTPRequestOperation *operation, NSError *error)
NSLog(@"Error: %@", error);
resError = error;
return;
];
[req waitUntilFinished];
NSLog(@"req.status: %d", req.response.statusCode);
NSLog(@"req.responseObj: %@", req.responseObject);
XCTAssertTrue(req.isFinished);
NSLog(@"resObj: %@", resObj);
NSLog(@"resError: %@", resError);
XCTAssertEqual([[req.responseObject objectForKey:@"success"] boolValue], YES);
XCTAssertEqual([[resObj objectForKey:@"success"] boolValue], YES);
控制台输出
测试用例“-[AppSupportTests testSanity]”已启动。 2014-04-29 16:45:07.424 xctest[72183:303] req.status: 200 2014-04-29 16:45:07.424 xctest[72183:303] req.responseObj: 成功 = 1; 2014-04-29 16:45:07.424 xctest[72183:303] resObj: (null) 2014-04-29 16:45:07.425 xctest[72183:303] resError: (null) /Users/jlujan/Code/AppSupport/AppSupportTests/AppSupportTests.m:114: 错误:-[AppSupportTests testSanity] : (([[resObj objectForKey:@"success"] boolValue]) 等于 (__objc_yes)) 失败:(" NO") 不等于 ("YES") 测试用例“-[AppSupportTests testSanity]”失败(0.003 秒)。【问题讨论】:
一旦后台操作完成,它可能会调用waitUntilFinished
,但这将是在调用回调之前。你可以尝试使用semaphore
s 到wait 直到你有signaled
对于异步测试我推荐Expecta顺便说一句。然后,您可以执行 expect([[req.responseObject objectForKey:@"success"] boolValue]).will.beTruthy();
之类的操作(尽管我不喜欢 beTruthy
语法!)。您只需确保设置了适当的超时时间[Expecta setAsynchronousTestTimeout:15];//seconds
这只是一个健全性检查,以确保我的实际代码没有导致它。使用这里的信号量示例***.com/questions/20476957/…,它会永远挂起。无论哪种方式,我都不能在我的实际代码中使用信号量方法。
@Rich,感谢您指向 Expecta。从长远来看,这比找出调度队列向南的方向更好的解决方案。
【参考方案1】:
根据 cmets 中的讨论,我们发现 waitUntilFinished
is once the background operation is complete,并且它不会等到调用完成块之后。
有一个更好的异步测试框架 - Expecta。 然后代替调用:
XCTAssertTrue(req.isFinished);
XCTAssertEqual([[resObj objectForKey:@"success"] boolValue], YES);
你可以这样做:
expect(req.isFinished).will.beTruthy();
expect([[resObj objectForKey:@"success"] boolValue]).will.beTruthy();
有lots of other matchers,只要确保在+setUp
方法中设置timeout 和+[Expecta setAsynchronousTestTimeout:]
。
【讨论】:
以上是关于AFNetworking 2.0 和单元测试的主要内容,如果未能解决你的问题,请参考以下文章