【问题标题】:How to init an object with stubbed values with OCMock如何使用 OCMock 初始化具有存根值的对象
【发布时间】:2011-09-16 03:49:57
【问题描述】:

如何对 init 方法中使用的方法存根?

我班的相关方法:

- (id)init
{
    self = [super init];
    if (self) {
        if (self.adConfigurationType == AdConfigurationTypeDouble) {
             [self configureForDoubleConfiguration];
        }
        else {
            [self configureForSingleConfiguration];
        }
    }
    return self;
}

- (AdConfigurationType)adConfigurationType
{
    if (adConfigurationType == NSNotFound) {
        if ((random()%2)==1) {
            adConfigurationType = AdConfigurationTypeSingle;
        }
        else {
            adConfigurationType = AdConfigurationTypeDouble;
        }
    }
    return adConfigurationType;
}

我的测试:

- (void)testDoubleConfigurationLayout
{
    id mockController = [OCMockObject mockForClass:[AdViewController class]];
    AdConfigurationType type = AdConfigurationTypeDouble;
    [[[mockController stub] andReturnValue:OCMOCK_VALUE(type)] adConfigurationType];

    id controller = [mockController init];

    STAssertNotNil([controller smallAdRight], @"Expected a value here");
    STAssertNotNil([controller smallAdRight], @"Expected a value here");
    STAssertNil([controller largeAd], @"Expected nil here");
}

我的结果:

由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“OCMockObject[AdViewController]:调用了意外的方法:smallAdRight”

那么我将如何访问 OCMockObject 中的 AdViewController?

【问题讨论】:

    标签: objective-c unit-testing ocmock


    【解决方案1】:

    如果您使用mockForClass: 方法,您将需要为模拟类中调用的每个方法提供存根实现。在您的第一次测试中使用 [controller smallAdRight] 调用它。

    相反,您可以使用 niceMockForClass: 方法,该方法将忽略任何未模拟的消息。

    另一种方法是实例化您的AdViewController,然后使用partialMockForObject: 方法为其创建部分模拟。这样,控制器类的内部将完成主要工作。

    不过……您是要测试 AdViewController 还是使用它的类?您似乎正在尝试模拟整个班级,然后测试它是否仍然正常运行。如果您想测试AdViewController 在注入某些值时的行为是否符合预期,那么您最好的选择很可能是partialMockForObject: 方法:

    - (void)testDoubleConfigurationLayout {     
      AdViewController *controller = [AdViewController alloc];
      id mock = [OCMockObject partialMockForObject:controller];
      AdConfigurationType type = AdConfigurationTypeDouble;
      [[[mock stub] andReturnValue:OCMOCK_VALUE(type)] adConfigurationType];
    
      // You'll want to call init after the object have been stubbed
      [controller init]
    
      STAssertNotNil([controller smallAdRight], @"Expected a value here");
      STAssertNotNil([controller smallAdRight], @"Expected a value here");
      STAssertNil([controller largeAd], @"Expected nil here");
    }
    

    【讨论】:

    • 对克劳斯的回答做了两个小修改,但基本上建议的测试实现现在应该通过了
    • 有时你的 init 方法只进行一次性配置,而你要测试的路径不会用这种方法执行。如果您只在第一行调用 [AdViewController alloc],这种测试通常也有效。
    • 再次阅读您的帖子,现在对我来说很明显,在控制器被模拟之前您不应该调用 init。我第一次没有注意到您实际上通过属性对 adConfigurationType 进行了隐式调用。我已经编辑了我的帖子以更好地反映这一点。
    • 上面的代码不正确。调用 [controller init] 将调用 OCPartialMockObject 上的 init 方法,而不是控制器对象上的 init 方法。要正确调用控制器 init,请使用 (void)[[(OCPartialMockObject *)controller realObject] init];
    • @Eric 因为模拟是部分模拟,它会将初始化消息转发给真正的控制器对象,因为它不是存根或预期的。
    猜你喜欢
    • 2013-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-15
    • 2014-06-17
    • 2019-08-28
    • 2014-05-18
    相关资源
    最近更新 更多