不能在 Xcode 5.0 中使用 OCMock 2.1+ 存根类方法

Posted

技术标签:

【中文标题】不能在 Xcode 5.0 中使用 OCMock 2.1+ 存根类方法【英文标题】:cannot stub class method with OCMock 2.1+ in Xcode 5.0 【发布时间】:2013-10-10 05:20:39 【问题描述】:

我知道 OCMock 版本 2.1+ supports 开箱即用的存根类方法。但由于某种原因,它不适用于我。为了确保我隔离了问题,我只是克隆了示例OCMock project(明确标记为版本 2.2.1)并简单地将其添加到 testMasterViewControllerDeletesItemsFromTableView 中:

id detailViewMock = [OCMockObject mockForClass:[DetailViewController class]];
[[[detailViewMock stub] andReturn:@"hello"] helloWorld]; 

DetailViewController.h我加了:

+ (NSString *)helloWorld;

DetailViewController.m:

+ (NSString *)helloWorld 
    return @"hello world";

但我不断收到错误:

*** -[NSProxy doesNotRecognize Selector:helloWorld] called!

要查看问题的演示,请克隆 this repo 以查看发生了什么。

【问题讨论】:

这看起来应该可以工作...我认为 DetailViewController.h 包含在您的测试中?你可以不使用模拟调用“helloWorld”吗? 是的 DetailViewController.h 包含在测试中,并将 Class 方法存根替换为常规存根(相同的 hello world 签名等)......但它不起作用...... @BenFlynn 我刚刚创建了一个非常简单的repo 来演示我遇到的问题。如果您能快速浏览一下,那就太好了! 【参考方案1】:

这应该可以正常工作。我刚刚在我的一个项目中尝试过,它在 Xcode5 上使用 XCTest,并且该代码通过了。

我会 1) 确保您使用的是最新版本的 OCMock(现在是 2.2.1;我认为在较新版本中对类方法和 Xcode5 都有一些修复),以及 2) 确保您的DetailViewController 类在运行时正确链接(即正确目标的一部分)。

在查看您的项目时,您的 DetailViewController 类是主应用程序的一部分,测试目标。对于 Xcode5,这似乎意味着该类的两个副本被编译并存在于运行时中,应用程序中的代码调用一个副本,而测试用例中的代码调用另一个副本。这曾经是一个链接器错误(重复符号),但无论好坏,链接器现在似乎默默地允许同一类(具有相同名称)的两个副本存在于 ObjC 运行时中。 OCMock 使用动态查找找到第一个(编译到应用程序中的那个),但测试用例直接链接到第二个副本(编译到测试包中的那个)。所以... OCMock 实际上并不是在嘲笑您认为的课程。

您可以看到这一点,只是为了笑,作为测试用例的一部分验证 [DetailViewController class] 不等于 NSClassFromString(@"DetailViewController") (第一个是直接链接的,第二个是动态的)。

要正确解决此问题,请在 DetailViewController.m 的“目标成员资格”中,取消选中测试目标。这样,运行时中只有一个类的副本,并且事情就像您期望的那样工作。测试包被加载到主应用程序中,因此所有主应用程序的类都应该可供包使用,而不必直接将它们编译到包中。类应该只是两个目标之一的一部分,而不是两者(一直如此)。

【讨论】:

经过这么长的回答后,我必须问:您是否在我提供的 repo 上尝试了您的解决方案?我只是仔细检查并确保 DetailViewController 确实是应用程序目标本身的一部分,并且 not 测试目标的一部分。我还检查了 [DetailViewcontroller class] 是否等于 NSClassFromString(@" DetailViewController").. 果然它们是(无论如何它们都返回字符串.. 不多说).. 你的答案没有用 是的,我下载了你的项目。 DetailViewController 是这两个目标的一部分;一旦我将它从测试目标中删除,测试就通过了。不过,我确实安装了更新的 OCMock。至于等号,我的意思是 [DetailViewController 类](从测试用例调用时)不会返回与 NSClassFromString(@"DetailViewController") 相同的指针——如果将它们与“==”进行比较,它们在您的项目,而通常它们应该是。 即使在上面看屏幕截图,我看到了“选择测试目标时”编译源(2项)“ - 一个是您的其他文件。第二个是DetailViewController.m。该屏幕截图中应该只有一项; DetailViewController 应仅在选择应用程序目标时显示在“编译源”区域中。 DetailViewController.m isn't part 在我的测试目标下的 2 个项目中.. 但我同意你的观点,我使用的库不是最新的.. 我只是克隆了示例ocmock github 帐户并 假设 它是最新的。我正在审查它和 lib turned out to be 2 岁!! 至于确定您拥有哪个版本...我认为其中没有嵌入版本字符串。但是你可以otool -av /path/to/libOCMock.a查看所有存档成员的编译日期,这提供了一个很好的线索。【参考方案2】:

你能展示你正在测试的代码吗? 这有效:

@interface DetailViewController : UIViewController

+ (NSString *) helloWorld;

@end

@implementation DetailViewController

+ (NSString *)helloWorld

    return @"hello world";


@end

测试:

- (void) test__stubbing_a_class_method

    id mockDetailViewController = [OCMockObject mockForClass:[DetailViewController class]];
    [[[mockDetailViewController stub] andReturn:@"hello"] helloWorld];

    STAssertEqualObjects([DetailViewController helloWorld], @"hello", nil);

【讨论】:

我刚刚创建了一个非常简单的repo 来演示我遇到的问题。如果您能快速浏览一下,那就太好了!【参考方案3】:

查看您的示例项目:

    您不应在测试目标中编译 DetailViewController.m。

    您的主要目标中不应有任何对 OCMock 的引用。

我从两个项目中删除了所有对 OCMock 的引用,然后只从源代码中包含了 OCMock,并且测试通过了就好了。我想你可能只是有一些环境冲突导致了你的问题。

【讨论】:

【参考方案4】:

虽然Carl Lindberg's 的答案是correct one,但我想我会在这里总结我们在他的答案的 cmets 中讨论的内容:

问题只是我使用的是过时的版本 强迫症。我到那里的原因是 b/c 上的说明 ocmock 页面简单地介绍我去抢 ios 示例关闭他们的 github 帐户并复制到 OCMock 库(他们甚至指示使用相同的目录结构)。 事实证明,他们示例中的库是over 2 years old!!。

要解决这个问题,只需在 shell 上运行 build.rb 脚本,如下所示: ruby build.rb。这将为您提供最新的 libOCMock.a 库,您可以简单地将其重新插入您的项目,瞧!一切都完成了!

【讨论】:

或者使用 Cocoapods!版本 2.3.3 非常适用于 Xcode 5 和 iOS 7 以及 XCTest。在那里运气不错。【参考方案5】:

只是使用

id detailViewMock = [OCMockObject niceMockForClass:[DetailViewController class]];

【讨论】:

以上是关于不能在 Xcode 5.0 中使用 OCMock 2.1+ 存根类方法的主要内容,如果未能解决你的问题,请参考以下文章

OCMock 没有传播到实现代码中 - iOS Xcode CocaPod

XCode - iOS - <OCMock/OCMock.h> 文件未找到

OCMock 不能存根分类方法

Xcode 4.6 不能正确识别 Xcode 5.0 创建的项目的链接框架

Xcode 5.0 .xib 文件?它在哪里?

如何使用 OCMock 测试类方法