1

我想通过单元测试验证IBoutlet我的控制器类中的所有 s 是否正确连接到 NIB 文件中。我想用 OCMock 来做这件事——即使我知道我可以简单地断言控制器变量不在nil加载 NIB 之后。这更多的是对流程如何工作的一般理解 - 据我所知,这也应该有效。

NIBOnOffSwitchCell作为其 File's Owner OnOffSwitchCellController。这是我的测试方法:

- (void) testIBOutletCellIsWiredToXib {
    id mockController = [OCMockObject mockForClass:[OnOffSwitchCellController class]];
    [[mockController expect] awakeAfterUsingCoder:OCMOCK_ANY];
    [[mockController expect] setValue:OCMOCK_ANY forKey:@"cell"];
    [[mockController expect] setValue:OCMOCK_ANY forKey:@"thelabel"];
    [[mockController expect] setValue:OCMOCK_ANY forKey:@"theswitch"];

    NSArray* nibContents = [guiBundle loadNibNamed:@"OnOffSwitchCell"
                                             owner:mockController
                                           options:nil];
    assertThat(nibContents, isNot(nil));
    assertThatInt([nibContents count], is(equalToInt(1)));
    assertThat([nibContents objectAtIndex:0], is(instanceOf([OnOffSwitchCell class])));

    [mockController verify];
}

guiBundle存在并被验证为有效的 NSBundle 对象。

据我了解loadNibNamed:owner:options:将反序列化NIB中的对象,调用awakeAfterUsingCoder:然后通过调用setValue:forKey:每个出口来设置出口。

我又添加了三个断言,以确保加载的 NIB 实际上包含正确的对象——当我放入真实控制器的实例时,这些断言通过了。但是当我使用如上所示的模拟时,它甚至没有走到这一步。相反,测试会因此崩溃:

测试用例“-[OnOffSwitchCellControllerTestCase testIBOutletCellIsWiredToXib]”已启动。
 2011-01-14 10:48:35.364 GTMTest[67797:903] *** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,
     原因:'OCMockObject [OnOffSwitchCellController]:
              调用了意外的方法:awakeAfterUsingCoder:<UINibDecoder: 0x500e800>
    预期:setValue:<OCMAnyConstraint:0x4c718e0> forKey:@"cell"
    预期:setValue:<OCMAnyConstraint:0x4c71ce0> forKey:@"thelabel"
    预期:setValue:<OCMAnyConstraint:0x4c71ed0> forKey:@"theswitch"'
*** 第一次抛出调用堆栈:
(
    0 核心基础 0x00e3dbe9 __exceptionPreprocess + 185
    1 libobjc.A.dylib 0x00f925c2 objc_exception_throw + 47
    2 核心基础 0x00e3db21 -[NSException 引发] + 17
    3 GTMTest 0x0001a049 -[OCMockObject 句柄UnRecordedInvocation:] + 322
    4 GTMTest 0x00019aca-[OCMockObject forwardInvocation:] + 77
    5 核心基础 0x00daf404 ___转发___ + 1124
    6 核心基础 0x00daef22 _CF_forwarding_prep_0 + 50
    7 UIKit 0x0062394a UINibDecoderDecodeObjectForValue + 2438
    8 UIKit 0x00624693-[UINibDecoder decodeObjectForKey:] + 398
    9 UIKit 0x0053cf43-[UIRuntimeConnection initWithCoder:] + 212
    10 UIKit 0x0053d4b1-[UIRuntimeEventConnection initWithCoder:] + 64
    11 UIKit 0x006239e4 UINibDecoderDecodeObjectForValue + 2592
    12 UIKit 0x006232dc UINibDecoderDecodeObjectForValue + 792
    13 UIKit 0x00624693-[UINibDecoder decodeObjectForKey:] + 398
    14 UIKit 0x0053c200 -[UINib 实例化WithOwner:选项:] + 804
    15 UIKit 0x0053e081-[NSBundle(UINSBundleAdditions) loadNibNamed:owner:options:] + 168
    16 GTMTest 0x000140dc-[OnOffSwitchCellControllerTestCase 测试IBOutletCellIsWiredToXib] + 503
    17 GTMTest 0x000041f3 -[SenTestCase 调用测试] + 163
    18 GTMTest 0x0000479a -[GTMTestCase 调用测试] + 146
    19 GTMTest 0x00003e90 -[SenTestCase 执行测试] + 37
    20 GTMTest 0x00002f3d -[GTMIPhoneUnitTestDelegate runTests] + 1413
    21 GTMTest 0x000028fb -[GTMIPhoneUnitTestDelegate applicationDidFinishLaunching:] + 197
    22 UIKit 0x00347253-[UIApplication_callInitializationDelegatesForURL:payload:suspended:] + 1252
    23 UIKit 0x0034955e-[UIApplication_runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 439
    24 UIKit 0x00348ef0 -[UIApplication_run] + 452
    25 UIKit 0x0035542e UIApplicationMain + 1160
    26 GTMTest 0x00003500 主要 + 104
    27 GTMTest 0x0000273d 开始 + 53
    28 ???0x00000002 0x0 + 2
)
在抛出“NSException”实例后调用终止

因此awakeAfterUsingCoder:,尽管我显然预料到了,但它却在抱怨这个电话是出乎意料的。

我还尝试消除这种期望并用一个不会报告多余方法调用的漂亮模拟替换模拟,但它仍然会中止并报告setValue:forKey:没有被调用。

我在这里想念什么?

4

2 回答 2

0

您是否尝试过在主系统线程上运行它?你不能在主线程之外实例化 UIKit 类。不确定 GTM 是如何做到的,但是使用 GHUnit,您可以将以下内容放入您的测试用例类中:

- (BOOL)shouldRunOnMainThread {
    /* Tell GHUnit to run on a separate thread */
    return YES;
}
于 2011-02-03T15:52:16.653 回答
-1

你实际上不能在单元测试中做任何视觉上的东西。视图从未实际加载。此外,您不需要测试awakeAfterUsingCoder被调用的内容。那是苹果的东西。通常,您的单元测试必须只测试您的逻辑。您可以放心地假设它awakeAfterUsingCoder是从 init 调用的,因为它是 Apple 的代码。你只需要确保你的方法被调用

于 2013-05-24T06:22:14.703 回答