如何在objective-C中使用OCMock的mock和verify方法?

发布于 2024-08-24 08:27:28 字数 794 浏览 5 评论 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

My problem is I am getting an error:

OCMckObject[NSNumberFormatter]:
expected method was not
invoked:setAllowsFloats:YES

I have written the following code:

(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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

凉墨 2024-08-31 08:27:28

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

Because your StringNumber implementation uses its own NSNumberFormatter object, not the one you created in your test case. You need to perform a classic refactoring operation called "dependency inversion", where your StringNumber object takes its formatter as a parameter or ivar instead of creating it internally. Then you can pass your mock formatter to it as part of your test.

风吹雨成花 2024-08-31 08:27:28

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

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

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

我倾向于使用模拟来:

  • 提取出依赖于特定环境/配置/数据集的依赖项 提取
  • 出在测试时不起作用的依赖项
  • 验证我的类在依赖代码行为的不同情况下的行为(例如,当依赖项返回 nil 时、当依赖项返回负值时、当依赖项抛出异常时等)

For the code sample you present, I wouldn't worry about mocking NSNumberFormatter. You're trying to verify that [StringNumber returnStringFromNumber:] returns an appropriate string. Your test shouldn't care in this case what method it uses to calculate that string, just that your method works as intended.

Since you just return the value returned by numberWithFloat:, and NSNumberFormatter is a utility class, you shouldn't mock it. Then if the behavior of numberWithFloat: changes in the future, your test will fail and you can decide whether to change the test or change your implementation.

If you mock NSNumberFormatter, you're basically just testing that your method will return whatever it returns, so if its behavior changes, the behavior of your class will change too, an you won't know it (at test time).

I tend to use mocks to:

  • factor out a dependency that is dependent on a particular environment/configuration/data set
  • factor out a dependency that won't work at test time
  • verify the behavior of my class for different cases of the dependent code's behavior (e.g., when dependency returns nil, when dependency returns negative value, when dependency throws exception, etc)
靑春怀旧 2024-08-31 08:27:28

您可以尝试动态重写 NSNumberFormatter 的 init 方法以返回您的模拟对象。在你的测试中,你必须从 idmockFormatter 创建一个 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

问候,
昆汀

You can try to dynamically override init method of NSNumberFormatter in order to return your mock object. In your tests you have to make an ivar from id mockFormatter. So your test method becomes:

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

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

Then you have to add a category for NSNumberFormatter class

@implementation NSNumberFormatter (mock)

- (id)init {
   id object;

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

   return object;
}

@end

So when init method of NSNumberFormatter will be called in your code under test, a mock object will be used if you have set it.

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

Regards,
Quentin

羞稚 2024-08-31 08:27:28

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

问候,
昆汀

Another idea:
You can try to make an ivar with formatter in StringNumber class.
Then, it will be possible to set formatter value using Key Value Coding from your tests.

Regards,
Quentin

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文