0

我的问题是我收到一个错误:

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

4 回答 4

4

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

于 2010-03-04T10:57:14.737 回答
1

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

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

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

我倾向于使用模拟来:

  • 分解出依赖于特定环境/配置/数据集的依赖项
  • 排除在测试时不起作用的依赖项
  • 针对依赖代码行为的不同情况验证我的类的行为(例如,当依赖项返回 nil 时,当依赖项返回负值时,当依赖项抛出异常时等)
于 2010-05-08T00:25:35.650 回答
0

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

问候,昆汀

于 2010-03-07T18:21:02.110 回答
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

问候,昆汀

于 2010-03-07T17:54:35.127 回答