OCUnit 测试在未测试的类中给出错误

Posted

技术标签:

【中文标题】OCUnit 测试在未测试的类中给出错误【英文标题】:OCUnit Tests giving errors in classes not being tested 【发布时间】:2011-03-16 21:57:12 【问题描述】:

我编写了一个 OCUnit 测试,它在 Xcode 3.2 中运行良好,现在我在 Xcode 4 中遇到了问题。它测试了 A 类中的单个静态函数 X,它不会调用任何类或函数(库函数之外)。唯一需要调用的其他代码是静态构造函数,它初始化两个静态变量,但同样,这些是不调用任何其他类或类A的函数的硬编码值(数组)。

然而,我必须为它导入的所有类包含 .m、.mm 和 .c 文件,以此类推,以解决 _OBJC_CLASS_$_ClassB",引用自: 错误。我不记得必须在 Xcode 3 中执行任何操作,但是很好,没问题,我可以编译它。现在,我收到来自类 B 的错误987654325@.

为什么在一个没有以任何方式引用的类上调用静态构造函数?我该如何解决这个问题,最好不要更改我的课程以适应测试?

更新

为了进一步弄清楚发生了什么,我注释掉了我所有的测试用例和#import "A.h",看看会发生什么。我添加了一个简单的单元测试:

- (void) testSomething 
    STAssertTrue(NO, @"did it work?");

仍然,我的 +[B initialize] 被调用并失败。似乎 OCUnit 正在遍历我的所有类,并且在此过程中调用了它们的 +initialize 方法。这对我来说毫无意义 - 如何禁用此行为?

这是调用我自己的代码之前的堆栈跟踪,以防万一:

#7  _class_initialize ()
#8  prepareForMethodLookup ()
#9  lookUpMethod ()
#10 objc_msgSend ()
#11 +[NSObject(SenTestRuntimeUtilities) senIsASuperclassOfClass:] ()
#12 +[NSObject(SenTestRuntimeUtilities) senAllSubclasses] ()
#13 +[SenTestSuite updateCache] ()
#14 +[SenTestSuite suiteForBundleCache] ()
#15 +[SenTestSuite testSuiteForBundlePath:] ()
#16 +[SenTestProbe specifiedTestSuite] ()
#17 +[SenTestProbe runTests:] ()
#18 <????> ()
#19 <????> ()
#20 <????> ()
#21 <????> ()

【问题讨论】:

【参考方案1】:

我在使用 xcode 4 和我的单元测试套件时遇到了许多问题。

我必须查看您的消息来源才能了解您的情况,但是 OCUnit 确实通过对类的反思来做很多事情。这就是运行以“test”开头的方法的方式。所以它检查你的类是有道理的,导致它们的类初始化器被触发。

我知道这可能不是您要寻找的答案,但如果 B 的 +initialize 中的错误是因为它依赖于某种预期的应用程序状态,那么您可能真的需要考虑重构 +initialize。由于这个确切的原因,它不应该依赖于类本身之外的任何东西......你无法保证它何时会触发或事物将处于什么状态。

【讨论】:

谢谢,这是有道理的,但是当测试或测试调用的 A 类中的函数不使用它的代码时,我什至需要包含 B.m 吗?我很确定我不必在 Xcode 3 中这样做,这就是为什么这从未出现过。 @Dov - 我仍然在拥抱其中的一些。不过,我会查看您的单元测试的“方案”。这些是 Xcode 4 的新功能,我发现它并不总是能适当地自动创建它们。如果您编辑该方案,则在您的 Build -> Build 选项中有一个特别是“Find Implicit Dependencies”的选项,它可能会试图拉入另一个项目。 关闭“查找隐式依赖项”没有帮助。现在我想我会确保我所有的 +initialize 函数独立于其他配置运行。在我的情况下,让这更难的是我正在使用 Core Data,这需要一堆共享状态。我将不得不以不同的方式处理它。 @Dov - 啊,是的,这会变得很复杂。我认为尽可能不依赖 +initializers 是一个很好的设计,所以希望这些更改不会对您的代码产生负面影响。无论如何,祝你好运。 我最终使用了一种到目前为止证明令人满意的解决方法。我将其发布为替代答案。【参考方案2】:

DougW 是正确的,我在 +initialize 方法中做的事情可能不应该在那里。无论如何,我还没有准备好重构它,只是为了让我的单元测试能够运行。

我使用下面的代码跳过了我的单元测试一开始就不需要的代码。我希望这对其他人有所帮助,因为它对任何函数都有用,而不仅仅是静态初始化程序。

+ (void)initialize

    if (NSClassFromString(@"SenTestSuite")) 
        NSLog(@"Not initializing 'B' class, because unit tests are running");
        return;
    

    ...

【讨论】:

OCUnit 遍历每个类(因此调用 +initialize:) 的行为一方面非常烦人;但另一方面,它会促使您提出更好的解耦设计!感谢您的问题和答案,它还帮助我解决了单元测试问题。 关于您的解决方法的另一个注意事项:它可能不是最好的方法,因为它会让您更改实际代码。我认为更好的解决方案是使用模拟框架。更多信息请访问:scn.sap.com/message/13574536

以上是关于OCUnit 测试在未测试的类中给出错误的主要内容,如果未能解决你的问题,请参考以下文章

OCUnit的测试用例类中是不是需要在公共接口中定义测试方法

OCUnit 测试未运行/未找到

链接两个或多个 SenTestCase

为啥我的班级用错误代码 138 使我的 OCUnit 测试用例崩溃?

为 iOS 构建 OCUnit 应用程序测试目标时出现链接器错误

在 OCUnit 中使用核心数据类的 Apple Mach-O 链接器错误