检查 IBOutlet 与 OCMock 的连接

发布于 2024-10-11 16:39:10 字数 5045 浏览 7 评论 0原文

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

NIB OnOffSwitchCell 的文件所有者为 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:< /code> 每一个。

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

 Test Case '-[OnOffSwitchCellControllerTestCase testIBOutletCellIsWiredToXib]' started.
 2011-01-14 10:48:35.364 GTMTest[67797:903] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
     reason: 'OCMockObject[OnOffSwitchCellController]:
              unexpected method invoked: awakeAfterUsingCoder:<UINibDecoder: 0x500e800> 
    expected:   setValue:<OCMAnyConstraint: 0x4c718e0> forKey:@"cell"
    expected:   setValue:<OCMAnyConstraint: 0x4c71ce0> forKey:@"thelabel"
    expected:   setValue:<OCMAnyConstraint: 0x4c71ed0> forKey:@"theswitch"'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x00e3dbe9 __exceptionPreprocess + 185
    1   libobjc.A.dylib                     0x00f925c2 objc_exception_throw + 47
    2   CoreFoundation                      0x00e3db21 -[NSException raise] + 17
    3   GTMTest                             0x0001a049 -[OCMockObject handleUnRecordedInvocation:] + 322
    4   GTMTest                             0x00019aca -[OCMockObject forwardInvocation:] + 77
    5   CoreFoundation                      0x00daf404 ___forwarding___ + 1124
    6   CoreFoundation                      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 instantiateWithOwner:options:] + 804
    15  UIKit                               0x0053e081 -[NSBundle(UINSBundleAdditions) loadNibNamed:owner:options:] + 168
    16  GTMTest                             0x000140dc -[OnOffSwitchCellControllerTestCase testIBOutletCellIsWiredToXib] + 503
    17  GTMTest                             0x000041f3 -[SenTestCase invokeTest] + 163
    18  GTMTest                             0x0000479a -[GTMTestCase invokeTest] + 146
    19  GTMTest                             0x00003e90 -[SenTestCase performTest] + 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 main + 104
    27  GTMTest                             0x0000273d start + 53
    28  ???                                 0x00000002 0x0 + 2
)
terminate called after throwing an instance of 'NSException'

因此,它抱怨对 awakeAfterUsingCoder: 的调用是意外的,尽管我显然预料到了。

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

我在这里缺少什么?

I want to verify with unit tests that all the IBoutlets in my controller class are correctly hooked up in the NIB file. I'd like to do this with OCMock - even though I know I could simply assert the controllers variables are not nil after loading the NIB. This is more a matter of general understanding of how the process works - as far as I understand it, this should be working, too.

The NIB OnOffSwitchCell has as its File's Owner OnOffSwitchCellController.
This is my test method:

- (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 exists and is verified to be a valid NSBundle object.

From what I understand loadNibNamed:owner:options: will deserialize the objects in the NIB, call awakeAfterUsingCoder: and then set the outlets by calling setValue:forKey: for each one.

I put in three more asserts to make sure the loaded NIB actually contains the correct objects - these pass OK when I put in an instance of the real controller. But when I use the mock as shown above, it does not even get this far. Instead, the test crashes with this:

 Test Case '-[OnOffSwitchCellControllerTestCase testIBOutletCellIsWiredToXib]' started.
 2011-01-14 10:48:35.364 GTMTest[67797:903] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
     reason: 'OCMockObject[OnOffSwitchCellController]:
              unexpected method invoked: awakeAfterUsingCoder:<UINibDecoder: 0x500e800> 
    expected:   setValue:<OCMAnyConstraint: 0x4c718e0> forKey:@"cell"
    expected:   setValue:<OCMAnyConstraint: 0x4c71ce0> forKey:@"thelabel"
    expected:   setValue:<OCMAnyConstraint: 0x4c71ed0> forKey:@"theswitch"'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x00e3dbe9 __exceptionPreprocess + 185
    1   libobjc.A.dylib                     0x00f925c2 objc_exception_throw + 47
    2   CoreFoundation                      0x00e3db21 -[NSException raise] + 17
    3   GTMTest                             0x0001a049 -[OCMockObject handleUnRecordedInvocation:] + 322
    4   GTMTest                             0x00019aca -[OCMockObject forwardInvocation:] + 77
    5   CoreFoundation                      0x00daf404 ___forwarding___ + 1124
    6   CoreFoundation                      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 instantiateWithOwner:options:] + 804
    15  UIKit                               0x0053e081 -[NSBundle(UINSBundleAdditions) loadNibNamed:owner:options:] + 168
    16  GTMTest                             0x000140dc -[OnOffSwitchCellControllerTestCase testIBOutletCellIsWiredToXib] + 503
    17  GTMTest                             0x000041f3 -[SenTestCase invokeTest] + 163
    18  GTMTest                             0x0000479a -[GTMTestCase invokeTest] + 146
    19  GTMTest                             0x00003e90 -[SenTestCase performTest] + 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 main + 104
    27  GTMTest                             0x0000273d start + 53
    28  ???                                 0x00000002 0x0 + 2
)
terminate called after throwing an instance of 'NSException'

So it is complaining the call to awakeAfterUsingCoder: as being unexpected, even though I clearly expected it.

I also tried removing that expectation and replacing the mock with a nice mock that will not report superfluous method calls, but then it still aborts and reports the setValue:forKey: not being called.

What am I missing here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甜点 2024-10-18 16:39:11

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

you cant actually do any visual stuff in unit tests. The views are never actually loaded. Also, you dont need to test that awakeAfterUsingCoder is invoked. Thats Apple's stuff. Typically your unit tests must only test your logic. You can safely assume that awakeAfterUsingCoder is invoked from init, because it's Apple's code. You just need to make sure your methods are invoked

沫离伤花 2024-10-18 16:39:10

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

- (BOOL)shouldRunOnMainThread {
    /* Tell GHUnit to run on a separate thread */
    return YES;
}

Have you tried running this on the main system thread? You cannot instance UIKit classes off the main thread. Not sure how GTM does it, but with GHUnit you can put the following into your test case class:

- (BOOL)shouldRunOnMainThread {
    /* Tell GHUnit to run on a separate thread */
    return YES;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文