OCMock:stub永远不会匹配签名

时间:2015-01-07 17:10:42

标签: ios objective-c unit-testing ocmock

我正在为一个数据源对象编写一个单元测试,其中包含惯用的委托对象。 此对象的作用是从某个Web服务获取某些数据,然后回调该委托以通知成功。这是代码:

   NSString *validProductId = @"34142977"; 
NSString *validSiteCode = @"someSiteCode";
[[dataSourceDelegateMock expect] dataSource:dataSource didFetchProductData:[OCMArg any] forProductWithId:validProductId];
[dataSource fetchProductWithId:validProductId andSiteCode:validSiteCode];

NSDate *runUntilDate = [NSDate dateWithTimeIntervalSinceNow:networkTimeOut];
while ([runUntilDate timeIntervalSinceNow] > 0) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:runUntilDate];
}
[dataSourceDelegateMock verify];

现在这个工作正常,只要网络在10秒内回复一些数据,测试就会成功。

我知道这不是某些人会测试网络代码的方式。有些人会这样做,有些人会称之为集成测试,但这不是我现在感兴趣的。

问题在于:上面的代码可以正常工作,但每次都会运行10秒,无论网络通常比这快得多。

我现在要做的是在我的while循环中添加另一个条件,其含义是"如果我们还没有超时并且如果网络还没有回复&#34 ;。通过这种方式,我可以在大多数情况下使测试执行得更快。

所以我修改了测试如下:

NSString *validProductId = @"34142977";
NSString *validSiteCode = @"someSiteCode";
__block BOOL dataSourceFetchedData = NO;
[[dataSourceDelegateMock expect] dataSource:dataSource didFetchProductData:[OCMArg any] forProductWithId:validProductId];
[[[dataSourceDelegateMock stub] andDo:^(NSInvocation * invocation) {
    dataSourceFetchedData = YES;
}] dataSource:dataSource didFetchProductData:[OCMArg any] forProductWithId:[OCMArg any]];
[dataSource fetchProductWithId:validProductId andSiteCode:validSiteCode];

NSDate *runUntilDate = [NSDate dateWithTimeIntervalSinceNow:networkTimeOut];
while ([runUntilDate timeIntervalSinceNow] > 0 && dataSourceFetchedData == NO) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:runUntilDate];
}
[dataSourceDelegateMock verify];

显然,这里的想法是,只要委托回调被发送到模拟委托对象(didFetchProductData ...),bool变量将被设置为YES并且while循环将终止,从而缩短测试的持续时间本身。

最后针对问题:无论我做什么,我都无法匹配将在运行时为委托回调调用的签名,因此我在块中放置的任何内容都将永远不会被执行。 测试仍然有效,但它不会更快。

经过大量调试后,我在validProductId变量上查明了问题。出于某种原因,我无法理解,返回的值永远不会与我在设置方法时设置的期望相匹配。我怎么知道 ?因为如果我将期望设置为nil,并强制数据源返回nil,一切都会正常工作。

我已经尝试了所有我能想到的东西,所以任何帮助都会受到很多赞赏。 这是回调的数据源方法:

(void) fetchProductWithId:(NSString *)productId andSiteCode:(NSString *)siteCode {

NSString *urlString = [NSString stringWithFormat:itemApiString,siteCode,productId];
NSURL *url = [NSURL URLWithString:urlString relativeToURL:self.baseUrl];
[self.requestManager GET:url.absoluteString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    [self.delegate dataSource:self didFetchProductData:responseObject forProductWithId:productId];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    [self.delegate dataSource:self didFailFetchProductDataForProductId:productId withError:error];
}];

}

正如您所看到的那样,数据源返回的是完全相同的productId,因此我真的无法理解为什么它与预期的存根方法不匹配。

非常感谢。

2 个答案:

答案 0 :(得分:1)

您可以合并expectstub,但互动不是大多数人所期望的。 expect处理对方法的第一次调用,一旦满足,就变为非活动状态,允许存根处理进一步的调用。

也就是说,可以添加预期的动作,我相信这就是你想要的;像这样:

[[[dataSourceDelegateMock expect] andDo:^(NSInvocation * invocation) {
    dataSourceFetchedData = YES;
}] dataSource:dataSource didFetchProductData:[OCMArg any]]

答案 1 :(得分:0)

这可能是存根和期望相同方法的问题。您可以尝试使用设置dataSourceFetchedData变量的块检查参数,并完全删除存根方法。

[[dataSourceDelegateMock expect] dataSource:dataSource didFetchProductData:[OCMArg checkWithBlock:^BOOL(id obj){
    dataSourceFetchedData = YES;
    return YES;
}] forProductWithId:validProductId];