我的问题是我收到错误:
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
答案 0 :(得分:4)
因为您的StringNumber
实现使用自己的NSNumberFormatter
对象,而不是您在测试用例中创建的对象。您需要执行称为“依赖项反转”的经典重构操作,其中StringNumber
对象将其格式化程序作为参数或ivar而不是在内部创建它。然后,您可以将模拟格式化程序作为测试的一部分传递给它。
答案 1 :(得分:1)
对于您提供的代码示例,我不担心模拟NSNumberFormatter。您正在尝试验证[StringNumber returnStringFromNumber:]
是否返回了适当的字符串。在这种情况下,您的测试不应该关注它用于计算该字符串的方法,只是您的方法按预期工作。
由于您只返回numberWithFloat:
返回的值,并且NSNumberFormatter是实用程序类,因此您不应该模拟它。然后,如果将来numberWithFloat:
的行为发生变化,您的测试将失败,您可以决定是更改测试还是更改实施。
如果你模拟NSNumberFormatter,你基本上只是测试你的方法将返回它返回的,所以如果它的行为改变,你的类的行为也会改变,你不会知道它(在测试时)。
我倾向于使用模拟:
答案 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。 然后,可以使用测试中的键值编码设置格式化程序值。
此致 昆汀