OCMock:存根从不匹配签名

Posted

技术标签:

【中文标题】OCMock:存根从不匹配签名【英文标题】:OCMock: stub never matches signature 【发布时间】:2015-01-07 17:10:42 【问题描述】:

我正在为一个数据源对象编写一个单元测试,它带有它惯用的委托对象。 该对象的作用是从某个 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 循环中添加另一个条件,其含义是“如果我们还没有超时并且如果网络还没有回复”。通过这种方式,我可以在大多数情况下使测试执行得更快。

所以我修改了测试如下:

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 与传递的完全相同,所以我真的不明白为什么这与预期的存根方法不匹配。

非常感谢。

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

【参考方案2】:

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

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

【讨论】:

谢谢,这行得通,但这是一种解决方法,而不是解决方案:(

以上是关于OCMock:存根从不匹配签名的主要内容,如果未能解决你的问题,请参考以下文章

OCMock:存根 @dynamic 属性

通过 OCMock 存根覆盖方法

OCMock 中的存根类方法

是否可以使用 OCMock 存根 NSProcessInfo?

OCMock: OCMPartialMock 对象调用实际方法而不是存根

正在测试的类的 OCMock 存根类方法