如何在Objective-C中使用模拟和验证OCMock的方法?

时间:2010-03-04 10:47:43

标签: objective-c ocmock

我的问题是我收到错误:

  

OCMckObject [NSNumberFormatter]:   预期的方法不是   调用:setAllowsFloats:YES

我写了以下代码:

(void) testReturnStringFromNumber
{
    id mockFormatter = [OCMockObject mockForClass:[NSNumberFormatter class]];
    StringNumber *testObject = [[StringNumber alloc] init];   

    [[mockFormatter expect] setAllowsFloats:YES];
    [testObject returnStringFromNumber:80.23456];
    [mockFormatter verify];
}


@implementation StringNumber

- (NSString *) returnStringFromNumber:(float)num
{
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    [formatter setAllowsFloats:YES];

    NSString *str= [formatter stringFromNumber:[NSNumber numberWithFloat:num]];

    [formatter release];
    return str;
}

@end

4 个答案:

答案 0 :(得分:4)

因为您的StringNumber实现使用自己的NSNumberFormatter对象,而不是您在测试用例中创建的对象。您需要执行称为“依赖项反转”的经典重构操作,其中StringNumber对象将其格式化程序作为参数或ivar而不是在内部创建它。然后,您可以将模拟格式化程序作为测试的一部分传递给它。

答案 1 :(得分:1)

对于您提供的代码示例,我不担心模拟NSNumberFormatter。您正在尝试验证[StringNumber returnStringFromNumber:]是否返回了适当的字符串。在这种情况下,您的测试不应该关注它用于计算该字符串的方法,只是您的方法按预期工作。

由于您只返回numberWithFloat:返回的值,并且NSNumberFormatter是实用程序类,因此您不应该模拟它。然后,如果将来numberWithFloat:的行为发生变化,您的测试将失败,您可以决定是更改测试还是更改实施。

如果你模拟NSNumberFormatter,你基本上只是测试你的方法将返回它返回的,所以如果它的行为改变,你的类的行为也会改变,你不会知道它(在测试时)。

我倾向于使用模拟:

  • 分解依赖于特定环境/配置/数据集的依赖关系
  • 将在测试时无效的依赖性分解出来
  • 验证我的类对于依赖代码行为的不同情况的行为(例如,当依赖性返回nil时,依赖性返回负值时,依赖性抛出异常时等)

答案 2 :(得分:0)

您可以尝试动态覆盖NSNumberFormatter的init方法,以便返回您的模拟对象。在你的测试中,你必须从id mockFormatter制作一个ivar。所以你的测试方法就变成了:

(void) testReturnStringFromNumber
{
    mockFormatter = [OCMockObject mockForClass:[NSNumberFormatter class]];
    StringNumber *testObject = [[StringNumber alloc] init];   

    [[mockFormatter expect] setAllowsFloats:YES];
    [testObject returnStringFromNumber:80.23456];
    [mockFormatter verify];
}

然后你必须为NSNumberFormatter类添加一个类别

@implementation NSNumberFormatter (mock)

- (id)init {
   id object;

   if (mockFormatter) {
      object = mockFormatter
   } else {
      object = invokeSupersequent();

   return object;
}

@end

因此,当您在测试的代码中调用NSNumberFormatter的init方法时,如果您设置了它,将使用模拟对象。

来源:   - http://cocoawithlove.com/2009/12/sample-mac-application-with-complete.html

此致 昆汀

答案 3 :(得分:0)

另一个想法: 你可以尝试在StringNumber类中使用格式化程序制作一个ivar。 然后,可以使用测试中的键值编码设置格式化程序值。

此致 昆汀