检查IBOutlet与OCMock的连接

时间:2011-01-14 09:55:26

标签: unit-testing ios mocking ocmock google-toolbox-for-mac

我想通过单元测试验证我的控制器类中的所有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:设置商店。

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

 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:没有被调用。

我在这里缺少什么?

2 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:-1)

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